home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / send.c < prev    next >
C/C++ Source or Header  |  1997-02-24  |  197KB  |  7,010 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: send.c,v 4.409 1996/07/08 18:10:18 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.  
  7.             T H E    P I N E    M A I L   S Y S T E M
  8.  
  9.    Laurence Lundblade and Mike Seibel
  10.    Networks and Distributed Computing
  11.    Computing and Communications
  12.    University of Washington
  13.    Administration Builiding, AG-44
  14.    Seattle, Washington, 98195, USA
  15.    Internet: lgl@CAC.Washington.EDU
  16.              mikes@CAC.Washington.EDU
  17.  
  18.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  19.  
  20.  
  21.    Pine and Pico are registered trademarks of the University of Washington.
  22.    No commercial use of these trademarks may be made without prior written
  23.    permission of the University of Washington.
  24.  
  25.    Pine, Pico, and Pilot software and its included text are Copyright
  26.    1989-1996 by the University of Washington.
  27.  
  28.    The full text of our legal notices is contained in the file called
  29.    CPYRIGHT, included with this distribution.
  30.  
  31.  
  32.    Pine is in part based on The Elm Mail System:
  33.     ***********************************************************************
  34.     *  The Elm Mail System  -  Revision: 2.13                             *
  35.     *                                                                     *
  36.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  37.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  38.     ***********************************************************************
  39.  
  40.  
  41.   ----------------------------------------------------------------------*/
  42.  
  43. /*======================================================================
  44.        send.c
  45.        Functions for composing and sending mail
  46.  
  47.  ====*/
  48.  
  49. #include "headers.h"
  50. #include "../c-client/smtp.h"
  51. #include "../c-client/nntp.h"
  52.  
  53.  
  54. #ifndef TCPSTREAM
  55. #define TCPSTREAM void
  56. #endif
  57.  
  58. #define    MIME_VER    "MIME-Version: 1.0\015\012"
  59.  
  60.  
  61. /*
  62.  * Internal Prototypes
  63.  */
  64. int       redraft PROTO((CONTEXT_S *, char *, ENVELOPE **, BODY **, char **,
  65.            char **, char **, PINEFIELD **));
  66. void       redraft_cleanup PROTO((char *, char *, MAILSTREAM *));
  67. void       outgoing2strings PROTO((METAENV *, BODY *, void **, PATMT **));
  68. void       strings2outgoing PROTO((METAENV *, BODY **, PATMT *));
  69. void       resolve_encoded_entries PROTO((ADDRESS *, ADDRESS *));
  70. int        write_fcc PROTO((char *, CONTEXT_S *, STORE_S *, char *));
  71. void       create_message_body PROTO((BODY **, PATMT *));
  72. int       check_addresses PROTO((METAENV *));
  73. ADDRESS   *generate_from PROTO(());
  74. void       free_attachment_list PROTO((PATMT **));
  75. PINEFIELD *get_dflt_custom_hdrs PROTO(());
  76. void       free_customs PROTO((PINEFIELD *));
  77. int        view_as_rich PROTO((char *, int));
  78. int        count_custom_hdrs PROTO((void));
  79. int        is_a_forbidden_hdr PROTO((char *));
  80. int        is_a_std_hdr PROTO((char *));
  81. int        hdr_has_custom_value PROTO((char *));
  82. void       set_default_hdrval PROTO((PINEFIELD *));
  83. void       customized_hdr_setup PROTO((PINEFIELD *));
  84. void       update_message_id PROTO((ENVELOPE *, char *));
  85. int       filter_message_text PROTO((char *, ENVELOPE *, BODY *, STORE_S **));
  86. long       message_format_for_pico PROTO((long, int (*)(int)));
  87. char      *send_exit_for_pico PROTO(());
  88. int       mime_type_for_pico PROTO((char *));
  89. void       pine_write_body_header PROTO((char **, BODY  *));
  90. void       pine_encode_body PROTO((BODY *));
  91. int        pine_rfc822_header PROTO((METAENV *, BODY *, soutr_t, TCPSTREAM *));
  92. int        pine_header_line PROTO((char *, char *, METAENV *, char *, soutr_t,
  93.                 TCPSTREAM *, int, int));
  94. int       pine_address_line PROTO((char *, char *, METAENV *, ADDRESS *,
  95.                  soutr_t, TCPSTREAM *, int, int));
  96. char      *prune_personal PROTO((ADDRESS *, char *));
  97. int        post_rfc822_output PROTO ((char *, ENVELOPE *, BODY *, soutr_t,
  98.                       TCPSTREAM *));
  99. int        pine_rfc822_output PROTO ((METAENV *, BODY *, soutr_t,TCPSTREAM *));
  100. long       pine_rfc822_output_body PROTO((BODY *,soutr_t,TCPSTREAM *));
  101. void       pine_free_body PROTO((BODY **));
  102. void       pine_free_body_data PROTO((BODY *));
  103. void       pine_free_body_part PROTO((PART **));
  104. long       pine_smtp_verbose PROTO((SMTPSTREAM *));
  105. void       pine_smtp_verbose_out PROTO((char *));
  106. int        call_mailer PROTO((METAENV *, BODY *));
  107. int        news_poster PROTO((METAENV *, BODY *));
  108. char      *tidy_smtp_mess PROTO((char *, char *, char *));
  109. char      *mime_stats PROTO((BODY *));
  110. void       mime_recur PROTO((BODY *));
  111. int        open_fcc PROTO((char *, CONTEXT_S **, int));
  112. long       pine_pipe_soutr PROTO((void *, char *));
  113. char      *pine_pipe_getline PROTO((void *));
  114. void       pine_pipe_close PROTO((void *));
  115. void       pine_pipe_abort PROTO((void *));
  116. int       sent_percent PROTO(());
  117. long       send_body_size PROTO((BODY *));
  118. void       set_body_size PROTO((BODY *));
  119. BODY      *first_text_8bit PROTO((BODY *));
  120. char      *set_mime_charset PROTO((int, char *));
  121.  
  122.  
  123. /*
  124.  * Buffer to hold pointers into pine data that's needed by pico. 
  125.  * Defined here so as not to take up room on the stack.  Better malloc'd?
  126.  */
  127. static    PICO    pbuf;
  128.  
  129.  
  130. /* 
  131.  * Storage object where the FCC (or postponed msg) is to be written.
  132.  * This is amazingly bogus.  Much work was done to put messages 
  133.  * together and encode them as they went to the tmp file for sendmail
  134.  * or into the SMTP slot (especially for for DOS, to prevent a temporary
  135.  * file (and needlessly copying the message).
  136.  * 
  137.  * HOWEVER, since there's no piping into c-client routines
  138.  * (particularly mail_append() which copies the fcc), the fcc will have
  139.  * to be copied to disk.  This global tells pine's copy of the rfc822
  140.  * output functions where to also write the message bytes for the fcc.
  141.  * With piping in the c-client we could just have two pipes to shove
  142.  * down rather than messing with damn copies.  FIX THIS!
  143.  *
  144.  * The function open_fcc, locates the actual folder and creates it if
  145.  * requested before any mailing or posting is done.
  146.  */
  147. static STORE_S *local_so      = NULL;
  148. static int    local_written = 0;
  149.  
  150.  
  151. /*
  152.  * Locally global pointer to stream used for sending/posting.
  153.  * It's also used to indicate when/if we write the Bcc: field in
  154.  * the header.
  155.  */
  156. static SMTPSTREAM *sending_stream = NULL;
  157. static struct hooks {
  158.     void    *soutr;                /* Line output routine */
  159.     void    *getline;            /* Line input routine */
  160.     void    *close;                /* Stream close routine */
  161.     void    *rfc822_out;            /* Message outputter */
  162. } sending_hooks;
  163. static FILE      *verbose_send_output = NULL;
  164. static long       send_bytes_sent, send_bytes_to_send;
  165. static char      *sending_filter_requested;
  166. static char       verbose_requested, background_requested;
  167. static METAENV      *send_header = NULL;
  168.  
  169.  
  170. #ifdef    DOS
  171. #define    FCC_SOURCE    FileStar
  172. #define    FCC_STREAM_MODE    OP_SHORTCACHE
  173. #else
  174. #define    FCC_SOURCE    CharStar
  175. #define    FCC_STREAM_MODE    0L
  176. #endif
  177.  
  178.  
  179. #define    INTRPT_PMT \
  180.         "Continue INTERRUPTED composition (answering \"n\" won't erase it)"
  181. #define    PSTPND_PMT \
  182.         "Continue postponed composition (answering \"No\" won't erase it)"
  183. #define    POST_PMT   \
  184.         "Posted message may go to thousands of readers. Really post"
  185. #define    INTR_DEL_FAIL    \
  186.        "Undelete messages to remain postponed, and then continue message"
  187. #define    INTR_DEL_PMT    "Deleted messages will be removed from folder.  Delete"
  188.  
  189. /*
  190.  * Since c-client preallocates, it's necessary here to define a limit
  191.  * such that we don't blow up in c-client (see rfc822_address_line()).
  192.  */
  193. #define    MAX_SINGLE_ADDR    MAILTMPLEN
  194.  
  195.  
  196. /*
  197.  * Macros to help sort out posting results
  198.  */
  199. #define    P_MAIL_WIN    0x01
  200. #define    P_MAIL_LOSE    0x02
  201. #define    P_MAIL_BITS    0x03
  202. #define    P_NEWS_WIN    0x04
  203. #define    P_NEWS_LOSE    0x08
  204. #define    P_NEWS_BITS    0x0C
  205. #define    P_FCC_WIN    0x10
  206. #define    P_FCC_LOSE    0x20
  207. #define    P_FCC_BITS    0x30
  208.  
  209.  
  210. #ifdef    DEBUG_PLUS
  211. #define    TIME_STAMP(str, l)    { \
  212.                   struct timeval  tp; \
  213.                   struct timezone tzp; \
  214.                   if(gettimeofday(&tp, &tzp) == 0) \
  215.                     dprint(l, (debugfile, \
  216.                        "\nKACHUNK (%s) : %.8s.%3.3ld\n", \
  217.                        str, ctime(&tp.tv_sec) + 11, \
  218.                        tp.tv_usec));\
  219.                 }
  220. #else
  221. #define    TIME_STAMP(str, l)
  222. #endif
  223.  
  224.  
  225.  
  226. /*----------------------------------------------------------------------
  227.     Compose screen (not forward or reply). Set up envelope, call composer
  228.   
  229.    Args: pine_state -- The usual pine structure
  230.  
  231.   Little front end for the compose screen
  232.   ---*/
  233. void
  234. compose_screen(pine_state)
  235.     struct pine *pine_state;
  236. {
  237.     ps_global = pine_state;
  238.     mailcap_free(); /* free resources we won't be using for a while */
  239.     pine_state->next_screen = pine_state->prev_screen;
  240.     compose_mail(NULL, NULL, NULL);
  241. }
  242.  
  243.  
  244.  
  245. /*----------------------------------------------------------------------
  246.      Format envelope for outgoing message and call editor
  247.  
  248.  Args:  given_to -- An address to send mail to (usually from command line 
  249.                        invocation)
  250.         fcc_arg  -- The fcc that goes with this address.
  251.  
  252.  If a "To" line is given format that into the envelope and get ready to call
  253.            the composer
  254.  If there's a message postponed, offer to continue it, and set it up,
  255.  otherwise just fill in the outgoing envelope as blank.
  256.  
  257.  NOTE: we ignore postponed and interrupted messages in nr mode
  258.  ----*/
  259. void 
  260. compose_mail(given_to, fcc_arg, inc_text_getc)
  261.     char    *given_to,
  262.         *fcc_arg;
  263.     gf_io_t  inc_text_getc;
  264. {
  265.     BODY      *body;
  266.     ENVELOPE  *outgoing = NULL;
  267.     PINEFIELD *custom   = NULL;
  268.     char      *p,
  269.           *fcc_to_free,
  270.           *fcc      = NULL,
  271.           *refs     = NULL,
  272.           *lcc      = NULL,
  273.           *sig      = NULL;
  274.     int        fcc_is_sticky = 0;
  275.  
  276.     dprint(1, (debugfile,
  277.                  "\n\n    ---- COMPOSE SCREEN (not in pico yet) ----\n"));
  278.  
  279.     /*-- Check for INTERRUPTED mail  --*/
  280.     if(!ps_global->anonymous && !given_to){
  281.     char         file_path[MAXPATH+1];
  282.     int         ret = 'n';
  283.  
  284.     /* build filename and see if it exists.  build_path creates
  285.      * an explicit local path name, so all c-client access is thru
  286.      * local drivers.
  287.      */
  288.     file_path[0] = '\0';
  289.     build_path(file_path,
  290.            ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
  291.                        : ps_global->home_dir,
  292.            INTERRUPTED_MAIL);
  293.  
  294.     /* check to see if the folder exists, the user wants to continue
  295.      * and that we can actually read something in...
  296.      */
  297.     if(folder_exists("[]", file_path) > 0
  298.        && (ret = want_to(INTRPT_PMT, 'y', 'x', NO_HELP, 0, 0)) == 'y'
  299.        && !redraft(NULL, file_path, &outgoing, &body, &fcc,
  300.                &refs, &lcc, &custom)){
  301.         return;
  302.     }
  303.     else if(ret == 'x'){
  304.         q_status_message(SM_ORDER, 0, 3, "Composition cancelled");
  305.         return;
  306.     }
  307.     }
  308.  
  309.     /*-- Check for postponed mail --*/
  310.     if(!outgoing                /* not replying/forwarding */
  311.        && !ps_global->anonymous            /* not special anon mode */
  312.        && !given_to                /* not command line send */
  313.        && ps_global->VAR_POSTPONED_FOLDER    /* folder to look in */
  314.        && ps_global->VAR_POSTPONED_FOLDER[0]){
  315.     CONTEXT_S   *p_cntxt;
  316.     int         ret = 'n';
  317.  
  318.     /* find default context to look for folder */
  319.     if(!(p_cntxt = default_save_context(ps_global->context_list)))
  320.       p_cntxt = ps_global->context_list;
  321.  
  322.     /* check to see if the folder exists, the user wants to continue
  323.      * and that we can actually read something in...
  324.      */
  325.     if(folder_exists(p_cntxt ? p_cntxt->context : "[]", 
  326.              ps_global->VAR_POSTPONED_FOLDER) > 0
  327.        && (ret = want_to(PSTPND_PMT, 'y', 'x', NO_HELP, 0, 0)) == 'y'
  328.        && !redraft(p_cntxt, ps_global->VAR_POSTPONED_FOLDER,
  329.                &outgoing, &body, &fcc, &refs, &lcc, &custom)){
  330.         return;
  331.     }
  332.     else if(ret == 'x'){
  333.         q_status_message(SM_ORDER, 0, 3, "Composition cancelled");
  334.         return;
  335.     }
  336.     }
  337.  
  338.     /*-- normal composition --*/
  339.     if(!outgoing){
  340.         /*=================  Compose new message ===============*/
  341.         body         = mail_newbody();
  342.         outgoing     = mail_newenvelope();
  343.  
  344.         if(given_to)
  345.       rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
  346.  
  347.         outgoing->message_id = generate_message_id(ps_global);
  348.  
  349.     /*
  350.      * The type of storage object allocated below is vitally
  351.      * important.  See SIMPLIFYING ASSUMPTION #37
  352.      */
  353.     if(body->contents.binary = (void *)so_get(PicoText,NULL,EDIT_ACCESS)){
  354.         char ch;
  355.  
  356.         if(inc_text_getc){
  357.         while((*inc_text_getc)(&ch))
  358.           if(!so_writec(ch, (STORE_S *)body->contents.binary)){
  359.               break;
  360.           }
  361.         }
  362.     }
  363.     else{
  364.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  365.                  "Problem creating space for message text.");
  366.         return;
  367.     }
  368.  
  369.     if(sig = get_signature()){
  370.         if(*sig)
  371.           so_puts((STORE_S *)body->contents.binary, sig);
  372.  
  373.         fs_give((void **)&sig);
  374.     }
  375.  
  376.     body->type = TYPETEXT;
  377.     }
  378.  
  379.     ps_global->prev_screen = compose_screen;
  380.     if(!(fcc_to_free = fcc))        /* Didn't pick up fcc, use given  */
  381.       fcc = fcc_arg;
  382.  
  383.     /*
  384.      * check whether a build_address-produced fcc is different from
  385.      * fcc.  If same, do nothing, if different, set sticky bit in pine_send.
  386.      */
  387.     if(fcc && outgoing->to){
  388.     char *tmp_fcc;
  389.  
  390.     tmp_fcc = get_fcc_based_on_to(outgoing->to);
  391.     if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
  392.       fcc_is_sticky++;  /* cause sticky bit to get set */
  393.  
  394.     if(tmp_fcc)
  395.       fs_give((void **)&tmp_fcc);
  396.     }
  397.  
  398.     pine_send(outgoing, &body, "COMPOSE MESSAGE", fcc, NULL, refs, NULL, lcc,
  399.           custom, fcc_is_sticky);
  400.  
  401.     if(fcc_to_free)
  402.       fs_give((void **)&fcc_to_free);
  403.  
  404.     if(lcc)
  405.       fs_give((void **)&lcc);
  406.  
  407.     mail_free_envelope(&outgoing);
  408.     pine_free_body(&body);
  409. }
  410.  
  411.  
  412.  
  413. /*----------------------------------------------------------------------
  414.     Given context and folder, offer the context for redraft...
  415.  
  416.  Args:    cntxt -- 
  417.     mbox -- 
  418.     outgoing -- 
  419.     body -- 
  420.     fcc -- 
  421.     custom -- 
  422.  
  423.  ----*/
  424. int
  425. redraft(cntxt, mbox, outgoing, body, fcc, ref_list, lcc, custom)
  426.     CONTEXT_S  *cntxt;
  427.     char       *mbox;
  428.     ENVELOPE  **outgoing;
  429.     BODY      **body;
  430.     char      **fcc;
  431.     char      **ref_list;
  432.     char      **lcc;
  433.     PINEFIELD **custom;
  434. {
  435.     MAILSTREAM    *stream;
  436.     ENVELOPE    *e = NULL;
  437.     BODY    *b;
  438.     PART        *part;
  439.     STORE_S    *so = NULL;
  440.     PINEFIELD   *pf;
  441.     gf_io_t     pc;
  442.     char    *extras, **fields, **values, *c_string, *p;
  443.     long     cont_msg = 1L;
  444.     int         i, j, rv = 0, pine_generated = 0;
  445. #define    TMP_BUF1    (tmp_20k_buf)
  446. #define    TMP_BUF2    (tmp_20k_buf + MAXPATH)
  447.  
  448.     /*
  449.      * if we have a context AND we're looking at an ambiguous name, 
  450.      * set the context string.  Otherwise, our context "relativeness"
  451.      * code in context.c may cause the fully qualified name to still
  452.      * get interpreted relative to the provided context...
  453.      */
  454.     c_string = (cntxt && context_isambig(mbox)) ? cntxt->context : "[]";
  455.  
  456.     /*
  457.      * Need to check here to see if the folder being opened for redraft
  458.      * is the currently opened folder.  If so, just use the current
  459.      * mail_stream...
  460.      */
  461.     context_apply(TMP_BUF1, c_string, mbox);
  462.     if(context_isambig(ps_global->cur_folder))
  463.       context_apply(TMP_BUF2, ps_global->context_current->context,
  464.             ps_global->cur_folder);
  465.     else
  466.       strcpy(TMP_BUF2, ps_global->cur_folder);
  467.  
  468.     if(!strcmp(TMP_BUF1, TMP_BUF2)){
  469.     stream = ps_global->mail_stream;
  470.     }
  471.     else{
  472.     if((stream = context_open(c_string, NULL, mbox, FCC_STREAM_MODE))
  473.        && (extras = strindex(stream->mailbox, '<'))
  474.        && !strcmp(extras + 1, "no_mailbox>"))
  475.       stream = NULL;
  476.     }
  477.  
  478.     if(stream){
  479.     /*
  480.      * If the we're manipulating the current folder, don't bother
  481.      * with index
  482.      */
  483.     if(!stream->nmsgs){
  484.         q_status_message(SM_ORDER | SM_DING, 3, 5,
  485.                  "Empty folder.  No messages really postponed!");
  486.         redraft_cleanup(c_string, mbox, stream);
  487.         return(0);
  488.     }
  489.     else if(stream == ps_global->mail_stream){
  490.         /*
  491.          * Since the user's got this folder already opened and they're
  492.          * on a selected message, pick that one rather than rebuild
  493.          * another index screen...
  494.          */
  495.         cont_msg = mn_m2raw(ps_global->msgmap,
  496.                 mn_get_cur(ps_global->msgmap));
  497.     }
  498.     else if(stream->nmsgs > 1L){        /* offer browser ? */
  499.         MSGNO_S *msgmap = NULL;
  500.  
  501.         mn_init(&msgmap, stream->nmsgs);
  502.         mn_set_cur(msgmap, 1L);
  503.  
  504.         clear_index_cache();
  505.         while(1){
  506.         rv = index_lister(ps_global, cntxt, mbox, stream, msgmap);
  507.         cont_msg = mn_get_cur(msgmap);
  508.         if(count_flagged(stream, "DELETED")
  509.            && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, 0, 0) == 'n'){
  510.             q_status_message(SM_ORDER, 3, 3, INTR_DEL_FAIL);
  511.             continue;
  512.         }
  513.  
  514.         break;
  515.         }
  516.  
  517.         clear_index_cache();
  518.         mn_give(&msgmap);
  519.  
  520.         if(rv){
  521.         redraft_cleanup(c_string, mbox, stream);
  522.         q_status_message(SM_ORDER, 0, 3, "Composition cancelled");
  523.         return(0);
  524.         }
  525.     }
  526.  
  527.     if((e = mail_fetchstructure(stream, cont_msg, &b))
  528.        && (so = (void *)so_get(PicoText, NULL, EDIT_ACCESS))){
  529.         gf_set_so_writec(&pc, so);
  530.  
  531.         *custom = get_dflt_custom_hdrs();
  532.         i       = (count_custom_hdrs() + 5) * sizeof(char *);
  533.  
  534.         /*
  535.          * Having these two fields separated isn't the slickest, but
  536.          * converting the pointer array for fetchheader_lines() to
  537.          * a list of structures or some such for simple_header_parse()
  538.          * is too goonie.  We could do something like reuse c-client's
  539.          * PARAMETER struct which is a simple char * pairing, but that
  540.          * doesn't make sense to pass to fetchheader_lines()...
  541.          */
  542.         fields = (char **) fs_get((size_t) i * sizeof(char *));
  543.         values = (char **) fs_get((size_t) i * sizeof(char *));
  544.         memset(fields, 0, (size_t) i * sizeof(char *));
  545.         memset(values, 0, (size_t) i * sizeof(char *));
  546.  
  547.         fields[i = 0] = "Fcc";        /* Fcc: special case */
  548.         fields[++i]   = "References";    /* References: too... */
  549.         fields[++i]   = "X-Post-Error";    /* posting errors too */
  550.         fields[++i]   = "Lcc";        /* Lcc: too... */
  551.  
  552.         /*
  553.          * BOGUS ALERT!  A c-client DEFICIENCY doesn't give us the
  554.          * newsgroups if the folder's accessed via IMAP, so if
  555.          * fetchstructure says there aren't any, we need to go to 
  556.          * the trouble of checking ourselves.  Sheesh.
  557.          */
  558.         if(!e->newsgroups)
  559.           fields[++i] = "Newsgroups";
  560.  
  561.         for(pf = *custom, ++i; pf && pf->name; pf = pf->next, i++)
  562.           fields[i] = pf->name;        /* assign custom fields */
  563.  
  564.         if(extras = xmail_fetchheader_lines(stream, cont_msg, fields)){
  565.         simple_header_parse(extras, fields, values);
  566.         fs_give((void **)&extras);
  567.  
  568.         /* translate RFC 1522 strings */
  569.         for(i = 3; fields[i]; i++)    /* starting with "Lcc" field */
  570.           if(values[i]){
  571.               char *charset;
  572.  
  573.               p = (char *)rfc1522_decode((unsigned char*)tmp_20k_buf,
  574.                          values[i], &charset);
  575.               if(charset)
  576.             fs_give((void **)&charset);
  577.  
  578.               if(p == tmp_20k_buf){
  579.               fs_give((void **)&values[i]);
  580.               values[i] = cpystr(p);
  581.               }
  582.           }
  583.  
  584.         for(pf = *custom, i = (e->newsgroups ? 4 : 5);
  585.             pf && pf->name;
  586.             pf = pf->next, i++){
  587.             if(values[i]){    /* use this instead of default */
  588.             if(pf->textbuf)
  589.               fs_give((void **)&pf->textbuf);
  590.  
  591.             pf->textbuf = values[i]; /* freed in pine_send! */
  592.             }
  593.             else if(pf->textbuf)  /* was erased before postpone */
  594.               fs_give((void **)&pf->textbuf);
  595.         }
  596.  
  597.         if(values[0])            /* If "Fcc:" was there...  */
  598.           pine_generated = 1;        /* we put it there? */
  599.  
  600.         if(fcc)                /* fcc: special case... */
  601.           *fcc = values[0] ? values[0] : cpystr("");
  602.  
  603.         if(ref_list)
  604.           *ref_list = values[1];
  605.  
  606.         if(values[2]){        /* x-post-error?!?1 */
  607.             q_status_message(SM_ORDER|SM_DING, 4, 4, values[2]);
  608.         }
  609.  
  610.         if(lcc)
  611.           *lcc = values[3];
  612.  
  613.         if(!e->newsgroups && values[4])
  614.           e->newsgroups = values[4];
  615.         }
  616.  
  617.         fs_give((void **)&fields);
  618.         fs_give((void **)&values);
  619.  
  620.         *outgoing = copy_envelope(e);
  621.  
  622.         /*
  623.          * Look at each empty address and see if the user has specified
  624.          * a default for that field or not.  If they have, that means
  625.          * they have erased it before postponing, so they won't want
  626.          * the default to come back.  If they haven't specified a default,
  627.          * then the default should be generated in pine_send.  We prevent
  628.          * the default from being assigned by assigning an empty address
  629.          * to the variable here.
  630.          *
  631.          * BUG: We should do this for custom Address headers, too, but
  632.          * there isn't such a thing yet.
  633.          *
  634.          * BUG: This doesn't work right for reply-to because c-client
  635.          * fills in reply-to even if there isn't one.
  636.          */
  637.         if(!(*outgoing)->to && hdr_has_custom_value("to"))
  638.           (*outgoing)->to = mail_newaddr();
  639.         if(!(*outgoing)->reply_to && hdr_has_custom_value("reply-to"))
  640.           (*outgoing)->reply_to = mail_newaddr();
  641.         if(!(*outgoing)->cc && hdr_has_custom_value("cc"))
  642.           (*outgoing)->cc = mail_newaddr();
  643.         if(!(*outgoing)->bcc && hdr_has_custom_value("bcc"))
  644.           (*outgoing)->bcc = mail_newaddr();
  645.  
  646.         if(!(*outgoing)->subject && hdr_has_custom_value("subject"))
  647.           (*outgoing)->subject = cpystr("");
  648.  
  649.         if(!pine_generated){
  650.         /*
  651.          * Now, this is interesting.  We should have found 
  652.          * the "fcc:" field if pine wrote the message being
  653.          * redrafted.  Hence, we probably can't trust the 
  654.          * "originator" type fields, so we'll blast them and let
  655.          * them get set later in pine_send.  This should allow
  656.          * folks with custom or edited From's and such to still
  657.          * use redraft reasonably, without inadvertently sending
  658.          * messages that appear to be "From" others...
  659.          */
  660.         if((*outgoing)->from)
  661.           mail_free_address(&(*outgoing)->from);
  662.  
  663.         /*
  664.          * Ditto for Reply-To and Sender...
  665.          */
  666.         if((*outgoing)->reply_to)
  667.           mail_free_address(&(*outgoing)->reply_to);
  668.  
  669.         if((*outgoing)->sender)
  670.           mail_free_address(&(*outgoing)->sender);
  671.  
  672.         /*
  673.          * Generate a fresh message id for pretty much the same
  674.          * reason From and such got wacked...
  675.          */
  676.         if((*outgoing)->message_id)
  677.           fs_give((void **)&(*outgoing)->message_id);
  678.  
  679.         (*outgoing)->message_id = generate_message_id(ps_global);
  680.         }
  681.  
  682.         if(b && b->type != TYPETEXT){    /* already TYPEMULTIPART */
  683.         *body               = copy_body(NULL, b);
  684.         part               = (*body)->contents.part;
  685.         part->body.type           = TYPETEXT;
  686.         part->body.contents.binary = (void *)so;
  687.         get_body_part_text(stream, &b->contents.part->body,
  688.                    cont_msg, "1", pc, "", NULL);
  689.  
  690.         if(!fetch_contents(stream, cont_msg, *body, *body))
  691.           q_status_message(SM_ORDER | SM_DING, 3, 4,
  692.                    "Error including all message parts");
  693.         }
  694.         else{
  695.         *body             = mail_newbody();
  696.         (*body)->type         = TYPETEXT;
  697.         (*body)->contents.binary = (void *)so;
  698.         get_body_part_text(stream, b, cont_msg, "1", pc, "", NULL);
  699.         }
  700.  
  701.         /* We have what we want, blast this message... */
  702.         if(!mail_elt(stream, cont_msg)->deleted)
  703.           mail_setflag(stream, long2string(cont_msg), "\\DELETED");
  704.  
  705.         redraft_cleanup(c_string, mbox, stream);
  706.         rv = 1;
  707.     }
  708.     else
  709.       rv = 0;
  710.     }
  711.  
  712.     return(rv);
  713. }
  714.  
  715.  
  716.  
  717. /*----------------------------------------------------------------------
  718.     Clear deleted messages from given stream and expunge if necessary
  719.  
  720. Args:    context -- 
  721.     folder --
  722.     stream -- 
  723.  
  724.  ----*/
  725. void
  726. redraft_cleanup(context, folder, stream)
  727.     char       *context;
  728.     char       *folder;
  729.     MAILSTREAM *stream;
  730. {
  731.     if(stream->nmsgs){
  732.     ps_global->expunge_in_progress = 1;
  733.     mail_expunge(stream);            /* clean out deleted */
  734.     ps_global->expunge_in_progress = 0;
  735.     }
  736.  
  737.     if(stream->nmsgs){                /* close and return */
  738.     if(stream != ps_global->mail_stream)
  739.       mail_close(stream);
  740.     }
  741.     else{                    /* close and delete folder */
  742.     if(stream == ps_global->mail_stream){
  743.         q_status_message1(SM_ORDER, 3, 7,
  744.                  "No more postponed messages, returning to \"%s\"",
  745.                  ps_global->inbox_name);
  746.         do_broach_folder(ps_global->inbox_name, ps_global->context_list);
  747.         ps_global->next_screen = mail_index_screen;
  748.     }
  749.     else
  750.       mail_close(stream);
  751.  
  752.     stream = context_same_stream(context, folder, ps_global->mail_stream);
  753.     if(!stream && ps_global->mail_stream != ps_global->inbox_stream)
  754.       stream = context_same_stream(context, folder,
  755.                        ps_global->inbox_stream);
  756.  
  757.     context_delete(context, stream, folder);
  758.     }
  759. }
  760.  
  761.  
  762.  
  763. /*----------------------------------------------------------------------
  764.     Parse the given header text for any given fields
  765.  
  766. Args:  text -- Text to parse for fcc and attachments refs
  767.        fields -- array of field names to look for
  768.        values -- array of pointer to save values to, returned NULL if
  769.                  fields isn't in text.
  770.  
  771. This function simply looks for the given fields in the given header
  772. text string.
  773. NOTE: newlines are expected CRLF, and we'll ignore continuations
  774.  ----*/
  775. void
  776. simple_header_parse(text, fields, values)
  777.     char   *text, **fields, **values;
  778. {
  779.     int   i, n;
  780.     char *p, *t;
  781.  
  782.     for(i = 0; fields[i]; i++)
  783.       values[i] = NULL;                /* clear values array */
  784.  
  785.     /*---- Loop until the end of the header ----*/
  786.     for(p = text; *p; ){
  787.     for(i = 0; fields[i]; i++)        /* find matching field? */
  788.       if(!struncmp(p, fields[i], (n=strlen(fields[i]))) && p[n] == ':'){
  789.           for(p += n + 1; *p; p++){        /* find start of value */
  790.           if(*p == '\015' && *(p+1) == '\012'
  791.              && !isspace((unsigned char) *(p+2)))
  792.             break;
  793.  
  794.           if(!isspace((unsigned char) *p))
  795.             break;            /* order here is key... */
  796.           }
  797.  
  798.           if(!values[i]){            /* if we haven't already */
  799.           values[i] = fs_get(strlen(text) + 1);
  800.           values[i][0] = '\0';        /* alloc space for it */
  801.           }
  802.  
  803.           if(*p && *p != '\015'){        /* non-blank value. */
  804.           t = values[i] + (values[i][0] ? strlen(values[i]) : 0);
  805.           while(*p){            /* check for cont'n lines */
  806.               if(*p == '\015' && *(p+1) == '\012'){
  807.               if(isspace((unsigned char) *(p+2))){
  808.                   p += 3;
  809.                   continue;
  810.               }
  811.               else
  812.                 break;
  813.               }
  814.  
  815.               *t++ = *p++;
  816.           }
  817.  
  818.           *t = '\0';
  819.           }
  820.  
  821.           break;
  822.       }
  823.  
  824.         /* Skip to end of line, what ever it was */
  825.         for(; *p ; p++)
  826.       if(*p == '\015' && *(p+1) == '\012'){
  827.           p += 2;
  828.           break;
  829.       }
  830.     }
  831. }
  832.  
  833.  
  834.  
  835. #if defined(DOS) || defined(OS2)
  836. /*----------------------------------------------------------------------
  837.     Verify that the necessary pieces are around to allow for
  838.     message sending under DOS
  839.  
  840. Args:  strict -- tells us if a remote stream is required before
  841.          sending is permitted.
  842.  
  843. The idea is to make sure pine knows enough to put together a valid 
  844. from line.  The things we MUST know are a user-id, user-domain and
  845. smtp server to dump the message off on.  Typically these are 
  846. provided in pine's configuration file, but if not, the user is
  847. queried here.
  848.  ----*/
  849. int
  850. dos_valid_from(strict)
  851.     int strict;
  852. {
  853.     char        prompt[80], answer[60], *p;
  854.     int         rc, i;
  855.     HelpType    help;
  856.  
  857.     /*
  858.      * If we're told to, require that a remote stream exist before
  859.      * permitting mail to get sent.  Someday this will be removed and
  860.      * sent mail sans a stream will get stuffed into an "outbox"...
  861.      */
  862.     if(strict && (!(ps_global->mail_stream
  863.             && !ps_global->mail_stream->anonymous
  864.             && mail_valid_net(ps_global->mail_stream->mailbox,
  865.                       ps_global->mail_stream->dtb,NULL,NULL))
  866.           && !(ps_global->inbox_stream
  867.                && ps_global->inbox_stream != ps_global->mail_stream
  868.                && !ps_global->inbox_stream->anonymous
  869.                && mail_valid_net(ps_global->inbox_stream->mailbox,
  870.                     ps_global->inbox_stream->dtb,NULL,NULL)))){
  871.     q_status_message(SM_ORDER, 3, 7,
  872.              "Can't send message without an open remote folder");
  873.     return(0);
  874.     }
  875.  
  876.     /*
  877.      * query for user name portion of address, use IMAP login
  878.      * name as default
  879.      */
  880.     if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
  881.     if(ps_global->mail_stream
  882.        && ps_global->mail_stream->dtb
  883.        && ps_global->mail_stream->dtb->name
  884.        && !strncmp(ps_global->mail_stream->dtb->name, "imap", 4)
  885.        && (p = imap_user(ps_global->mail_stream)))
  886.       strcpy(answer, p);
  887.     else
  888.       answer[0] = '\0';
  889.  
  890.     sprintf(prompt,"User-id for From address : "); 
  891.  
  892.     help = NO_HELP;
  893.     while(1) {
  894.         rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,79,
  895.                     1,0,prompt,NULL,help,0);
  896.         if(rc == 2)
  897.           continue;
  898.  
  899.         if(rc == 3){
  900.         help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
  901.         continue;
  902.         }
  903.  
  904.         if(rc != 4)
  905.           break;
  906.     }
  907.  
  908.     if(rc == 1 || (rc == 0 && !answer)) {
  909.         q_status_message(SM_ORDER, 3, 4,
  910.            "Send cancelled (User-id must be provided before sending)");
  911.         return(0);
  912.     }
  913.  
  914.     /* save the name */
  915.     sprintf(prompt, "Preserve %s as \"user-id\" in PINERC", answer);
  916.     if(ps_global->blank_user_id
  917.        && want_to(prompt, 'y', 'n', NO_HELP, 0, 0) == 'y'){
  918.         set_variable(V_USER_ID, answer, 1);
  919.     }
  920.     else{
  921.             fs_give((void **)&(ps_global->VAR_USER_ID));
  922.         ps_global->VAR_USER_ID = cpystr(answer);
  923.     }
  924.     }
  925.  
  926.     /* query for personal name */
  927.     if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'){
  928.     answer[0] = '\0';
  929.     sprintf(prompt,"Personal name for From address : "); 
  930.  
  931.     help = NO_HELP;
  932.     while(1) {
  933.         rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,79,
  934.                     1,0,prompt,NULL,help,0);
  935.         if(rc == 2)
  936.           continue;
  937.  
  938.         if(rc == 3){
  939.         help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
  940.         continue;
  941.         }
  942.  
  943.         if(rc != 4)
  944.           break;
  945.         }
  946.  
  947.     if(rc == 0 && answer){        /* save the name */
  948.         sprintf(prompt, "Preserve %s as \"personal-name\" in PINERC",
  949.             answer);
  950.         if(ps_global->blank_personal_name
  951.            && want_to(prompt, 'y', 'n', NO_HELP, 0, 0) == 'y'){
  952.         set_variable(V_PERSONAL_NAME, answer, 1);
  953.         }
  954.         else{
  955.             fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
  956.         ps_global->VAR_PERSONAL_NAME = cpystr(answer);
  957.         }
  958.     }
  959.     }
  960.  
  961.     /* 
  962.      * query for host/domain portion of address, using IMAP
  963.      * host as default
  964.      */
  965.     if(ps_global->blank_user_domain 
  966.        || ps_global->maildomain == ps_global->localdomain
  967.        || ps_global->maildomain == ps_global->hostname){
  968.     if(ps_global->inbox_name[0] == '{'){
  969.         for(i=0;ps_global->inbox_name[i+1] != '}'; i++)
  970.         answer[i] = ps_global->inbox_name[i+1];
  971.  
  972.         answer[i] = '\0';
  973.     }
  974.     else
  975.       answer[0] = '\0';
  976.  
  977.     sprintf(prompt,"Host/domain for From address : "); 
  978.  
  979.     help = NO_HELP;
  980.     while(1) {
  981.         rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,79,
  982.                     1,0,prompt,NULL,help,0);
  983.         if(rc == 2)
  984.           continue;
  985.  
  986.         if(rc == 3){
  987.         help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
  988.         continue;
  989.         }
  990.  
  991.         if(rc != 4)
  992.           break;
  993.     }
  994.  
  995.     if(rc == 1 || (rc == 0 && !answer)) {
  996.         q_status_message(SM_ORDER, 3, 4,
  997.       "Send cancelled (Host/domain name must be provided before sending)");
  998.         return(0);
  999.     }
  1000.  
  1001.     /* save the name */
  1002.     sprintf(prompt, "Preserve %s as \"user-domain\" in PINERC",
  1003.         answer);
  1004.     if(!ps_global->userdomain && !ps_global->blank_user_domain
  1005.        && want_to(prompt, 'y', 'n', NO_HELP, 0, 0) == 'y'){
  1006.         set_variable(V_USER_DOMAIN, answer, 1);
  1007.         fs_give((void **)&(ps_global->maildomain));    /* blast old val */
  1008.         ps_global->userdomain      = cpystr(answer);
  1009.         ps_global->maildomain      = ps_global->userdomain;
  1010.     }
  1011.     else{
  1012.             fs_give((void **)&(ps_global->maildomain));
  1013.             ps_global->userdomain = cpystr(answer);
  1014.         ps_global->maildomain = ps_global->userdomain;
  1015.     }
  1016.     }
  1017.  
  1018.     /* check for smtp server */
  1019.     if(!ps_global->VAR_SMTP_SERVER ||
  1020.        !ps_global->VAR_SMTP_SERVER[0] ||
  1021.        !ps_global->VAR_SMTP_SERVER[0][0]){
  1022.     char **list;
  1023.  
  1024.     if(ps_global->inbox_name[0] == '{'){
  1025.         for(i=0;ps_global->inbox_name[i+1] != '}'; i++)
  1026.           answer[i] = ps_global->inbox_name[i+1];
  1027.         answer[i] = '\0';
  1028.     }
  1029.     else
  1030.           answer[0] = '\0';
  1031.  
  1032.         sprintf(prompt,"SMTP server to forward message : "); 
  1033.  
  1034.     help = NO_HELP;
  1035.         while(1) {
  1036.             rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,79,
  1037.                     1,0,prompt,NULL,help,0);
  1038.             if(rc == 2)
  1039.                   continue;
  1040.  
  1041.         if(rc == 3){
  1042.         help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
  1043.         continue;
  1044.         }
  1045.  
  1046.             if(rc != 4)
  1047.                   break;
  1048.         }
  1049.  
  1050.         if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
  1051.             q_status_message(SM_ORDER, 3, 4,
  1052.            "Send cancelled (SMTP server must be provided before sending)");
  1053.             return(0);
  1054.         }
  1055.  
  1056.     /* save the name */
  1057.         list    = (char **) fs_get(2 * sizeof(char *));
  1058.     list[0] = cpystr(answer);
  1059.     list[1] = NULL;
  1060.     set_variable_list(V_SMTP_SERVER, list);
  1061.     fs_give((void *)&list[0]);
  1062.     fs_give((void *)list);
  1063.     }
  1064.  
  1065.     return(1);
  1066. }
  1067. #endif
  1068.  
  1069.  
  1070. /* this is for initializing the fixed header elements in pine_send() */
  1071. /*
  1072. prompt::name::help::prlen::maxlen::realaddr::
  1073. builder::affected_entry::next_affected::selector::key_label::
  1074. display_it::break_on_comma::is_attach::rich_header::only_file_chars::
  1075. single_space::sticky::dirty::start_here::KS_ODATAVAR
  1076. */
  1077. static struct headerentry he_template[]={
  1078.   {"From    : ",  "From",        h_composer_from,       10, 0, NULL,
  1079.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1080.    0, 1, 0, 1, 0, 1, 0, 0, 0, KS_TOADDRBOOK},
  1081.   {"Reply-To: ",  "Reply To",    h_composer_reply_to,   10, 0, NULL,
  1082.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1083.    0, 1, 0, 1, 0, 1, 0, 0, 0, KS_TOADDRBOOK},
  1084.   {"To      : ",  "To",          h_composer_to,         10, 0, NULL,
  1085.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1086.    0, 1, 0, 0, 0, 1, 0, 0, 0, KS_TOADDRBOOK},
  1087.   {"Cc      : ",  "Cc",          h_composer_cc,         10, 0, NULL,
  1088.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1089.    0, 1, 0, 0, 0, 1, 0, 0, 0, KS_TOADDRBOOK},
  1090.   {"Bcc     : ",  "Bcc",         h_composer_bcc,        10, 0, NULL,
  1091.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1092.    0, 1, 0, 1, 0, 1, 0, 0, 0, KS_TOADDRBOOK},
  1093.   {"Newsgrps: ",  "Newsgroups",  h_composer_news,        10, 0, NULL,
  1094.    news_build,    NULL, NULL, news_group_selector,  "To NwsGrps",
  1095.    0, 1, 0, 1, 0, 1, 0, 0, 0, KS_NONE},
  1096.   {"Fcc     : ",  "Fcc",         h_composer_fcc,        10, 0, NULL,
  1097.    NULL,          NULL, NULL, folders_for_fcc,      "To Fldrs",
  1098.    0, 0, 0, 1, 1, 1, 0, 0, 0, KS_NONE},
  1099.   {"Lcc     : ",  "Lcc",         h_composer_lcc,        10, 0, NULL,
  1100.    build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk",
  1101.    0, 1, 0, 1, 0, 1, 0, 0, 0, KS_NONE},
  1102.   {"Attchmnt: ",  "Attchmnt",    h_composer_attachment, 10, 0, NULL,
  1103.    NULL,          NULL, NULL, NULL,                 "To Files",
  1104.    0, 1, 1, 0, 0, 1, 0, 0, 0, KS_NONE},
  1105.   {"Subject : ",  "Subject",     h_composer_subject,    10, 0, NULL,
  1106.    valid_subject, NULL, NULL, NULL,                 NULL,
  1107.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1108.   {"",            "References",  NO_HELP,               10, 0, NULL,
  1109.    NULL,          NULL, NULL, NULL,                 NULL,
  1110.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1111.   {"",            "Date",        NO_HELP,               10, 0, NULL,
  1112.    NULL,          NULL, NULL, NULL,                 NULL,
  1113.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1114.   {"",            "In-Reply-To", NO_HELP,               10, 0, NULL,
  1115.    NULL,          NULL, NULL, NULL,                 NULL,
  1116.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1117.   {"",            "Message-ID",  NO_HELP,               10, 0, NULL,
  1118.    NULL,          NULL, NULL, NULL,                 NULL,
  1119.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1120.   {"",            "To",          NO_HELP,               10, 0, NULL,
  1121.    NULL,          NULL, NULL, NULL,                 NULL,
  1122.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1123.   {"",            "X-Post-Error",NO_HELP,               10, 0, NULL,
  1124.    NULL,          NULL, NULL, NULL,                 NULL,
  1125.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1126. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1127.   {"",            "Sender",      NO_HELP,               10, 0, NULL,
  1128.    NULL,          NULL, NULL, NULL,                 NULL,
  1129.    0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
  1130. #endif
  1131. };
  1132.  
  1133. static struct headerentry he_custom_addr_templ={
  1134.    NULL,          NULL,          h_composer_custom_addr,10, 0, NULL,
  1135.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1136.    0, 1, 0, 1, 0, 1, 0, 0, 0, KS_TOADDRBOOK};
  1137. static struct headerentry he_custom_free_templ={
  1138.    NULL,          NULL,          h_composer_custom_free,10, 0, NULL,
  1139.    NULL,          NULL, NULL, NULL,                 NULL,
  1140.    0, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE};
  1141.  
  1142. /*
  1143.  * Note, these are in the same order in the he_template and pf_template arrays.
  1144.  */
  1145. #define N_FROM    0
  1146. #define N_REPLYTO 1
  1147. #define N_TO      2
  1148. #define N_CC      3
  1149. #define N_BCC     4
  1150. #define N_NEWS    5
  1151. #define N_FCC     6
  1152. #define N_LCC     7
  1153. #define N_ATTCH   8
  1154. #define N_SUBJ    9
  1155. #define N_REF     10
  1156. #define N_DATE    11
  1157. #define N_INREPLY 12
  1158. #define N_MSGID   13
  1159. #define N_NOBODY  14
  1160. #define    N_POSTERR 15
  1161. #define N_SENDER  16
  1162.  
  1163. /* this is used in pine_send and pine_simple_send */
  1164. /* name::type::canedit::writehdr::localcopy::rcptto */
  1165. static PINEFIELD pf_template[] = {
  1166.   {"From",        Address,    0, 1, 1, 0},
  1167.   {"Reply-To",    Address,    0, 1, 1, 0},
  1168.   {"To",          Address,    1, 1, 1, 1},
  1169.   {"cc",          Address,    1, 1, 1, 1},
  1170.   {"bcc",         Address,    1, 0, 1, 1},
  1171.   {"Newsgroups",  FreeText,    1, 1, 1, 0},
  1172.   {"Fcc",         Fcc,        1, 0, 0, 0},
  1173.   {"Lcc",         Address,    1, 0, 1, 1},
  1174.   {"Attchmnt",    Attachment,    1, 1, 1, 0},
  1175.   {"Subject",     Subject,    1, 1, 1, 0},
  1176.   {"References",  FreeText,    0, 0, 0, 0},
  1177.   {"Date",        FreeText,    0, 1, 1, 0},
  1178.   {"In-Reply-To", FreeText,    0, 1, 1, 0},
  1179.   {"Message-ID",  FreeText,    0, 1, 1, 0},
  1180.   {"To",          Address,    0, 0, 0, 0},    /* N_NOBODY */
  1181.   {"X-Post-Error",FreeText,    0, 0, 0, 0},    /* N_POSTERR */
  1182. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1183.   {"X-Sender",    Address,    0, 1, 1, 0},
  1184. #endif
  1185.   {NULL,         FreeText}
  1186. };
  1187.  
  1188.  
  1189. /*----------------------------------------------------------------------
  1190.      Get addressee for message, then post message
  1191.  
  1192.   Args:  outgoing -- Partially formatted outgoing ENVELOPE
  1193.          body     -- Body of outgoing message
  1194.         prmpt_who -- Optional prompt for optionally_enter call
  1195.         prmpt_cnf -- Optional prompt for confirmation call
  1196.     used_tobufval -- The string that the to was eventually set equal to.
  1197.               This gets passed back if non-NULL on entry.
  1198.     prompt_for_to -- Allow user to change recipient
  1199.  
  1200.   Result: message "To: " field is provided and message is sent or cancelled.
  1201.  
  1202.   Fields:
  1203.      remail                -
  1204.      return_path           -
  1205.      date                 added here
  1206.      from                 added here
  1207.      sender                -
  1208.      reply_to              -
  1209.      subject              passed in, NOT edited but maybe canonized here
  1210.      to                   possibly passed in, edited and canonized here
  1211.      cc                    -
  1212.      bcc                   -
  1213.      in_reply_to           -
  1214.      message_id            -
  1215.   
  1216. Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
  1217. with the first part TYPETEXT! All newlines in the text here also end with
  1218. CRLF.
  1219.  
  1220. Returns 0 on success, -1 on failure.
  1221.   ----*/
  1222. int
  1223. pine_simple_send(outgoing, body, prmpt_who, prmpt_cnf, used_tobufval,
  1224.          prompt_for_to)
  1225.     ENVELOPE  *outgoing;  /* envelope for outgoing message */
  1226.     BODY     **body;   
  1227.     char      *prmpt_who, *prmpt_cnf;
  1228.     char     **used_tobufval;
  1229.     int        prompt_for_to;
  1230. {
  1231.     char     **tobufp, *p;
  1232.     void      *messagebuf;
  1233.     int        done = 0, retval = 0;
  1234.     int        cnt, i, resize_len, result;
  1235.     PINEFIELD *pfields, *pf, **sending_order;
  1236.     METAENV    header;
  1237.     HelpType   help;
  1238.     ESCKEY_S   ekey[2];
  1239.     BUILDER_ARG ba_fcc;
  1240.  
  1241.     dprint(1, (debugfile,"\n === simple send called === \n"));
  1242.  
  1243.     ba_fcc.tptr = NULL; ba_fcc.next = NULL; ba_fcc.xtra = NULL;
  1244.  
  1245.     /* how many fields are there? */
  1246.     cnt = (sizeof(pf_template)/sizeof(pf_template[0])) - 1;
  1247.  
  1248.     /* temporary PINEFIELD array */
  1249.     i = (cnt + 1) * sizeof(PINEFIELD);
  1250.     pfields = (PINEFIELD *)fs_get((size_t) i);
  1251.     memset(pfields, 0, (size_t) i);
  1252.  
  1253.     i             = (cnt + 1) * sizeof(PINEFIELD *);
  1254.     sending_order = (PINEFIELD **)fs_get((size_t) i);
  1255.     memset(sending_order, 0, (size_t) i);
  1256.  
  1257.     header.env           = outgoing;
  1258.     header.local         = pfields;
  1259.     header.custom        = NULL;
  1260.     header.sending_order = sending_order;
  1261.  
  1262.     /*----- Fill in a few general parts of the envelope ----*/
  1263.     if(!outgoing->date){
  1264.     rfc822_date(tmp_20k_buf);        /* format and copy new date */
  1265.     outgoing->date = cpystr(tmp_20k_buf);
  1266.     }
  1267.  
  1268.     outgoing->from      = generate_from();
  1269.     outgoing->return_path = rfc822_cpy_adr(outgoing->from);
  1270.  
  1271. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1272. #define NN 3
  1273. #else
  1274. #define NN 2
  1275. #endif
  1276.     /* initialize pfield */
  1277.     pf = pfields;
  1278.     for(i=0; i < cnt; i++, pf++){
  1279.  
  1280.         pf->name        = cpystr(pf_template[i].name);
  1281.     if(i == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
  1282.       /* slide string over so it is Sender instead of X-Sender */
  1283.       for(p=pf->name; *(p+1); p++)
  1284.         *p = *(p+2);
  1285.  
  1286.         pf->type        = pf_template[i].type;
  1287.     pf->canedit     = pf_template[i].canedit;
  1288.     pf->rcptto      = pf_template[i].rcptto;
  1289.     pf->writehdr    = pf_template[i].writehdr;
  1290.     pf->localcopy   = pf_template[i].localcopy;
  1291.         pf->he          = NULL; /* unused */
  1292.         pf->next        = pf + 1;
  1293.  
  1294.         switch(pf->type){
  1295.           case FreeText:
  1296.             switch(i){
  1297.               case N_NEWS:
  1298.         pf->text        = &outgoing->newsgroups;
  1299.         sending_order[0]    = pf;
  1300.                 break;
  1301.  
  1302.               case N_DATE:
  1303.         pf->text        = &outgoing->date;
  1304.         sending_order[1]    = pf;
  1305.                 break;
  1306.  
  1307.               case N_INREPLY:
  1308.         pf->text        = &outgoing->in_reply_to;
  1309.         sending_order[NN+9]    = pf;
  1310.                 break;
  1311.  
  1312.               case N_MSGID:
  1313.         pf->text        = &outgoing->message_id;
  1314.         sending_order[NN+10]    = pf;
  1315.                 break;
  1316.  
  1317.               case N_REF: /* won't be used here */
  1318.         sending_order[NN+11]    = pf;
  1319.                 break;
  1320.  
  1321.               case N_POSTERR: /* won't be used here */
  1322.         sending_order[NN+12]    = pf;
  1323.                 break;
  1324.  
  1325.               default:
  1326.                 q_status_message1(SM_ORDER,3,3,
  1327.             "Internal error: 1)FreeText header %d", (void *)i);
  1328.                 break;
  1329.             }
  1330.  
  1331.             break;
  1332.  
  1333.           case Attachment:
  1334.             break;
  1335.  
  1336.           case Address:
  1337.             switch(i){
  1338.           case N_FROM:
  1339.         sending_order[2]    = pf;
  1340.         pf->addr        = &outgoing->from;
  1341.         break;
  1342.  
  1343.           case N_TO:
  1344.         sending_order[NN+2]    = pf;
  1345.         pf->addr        = &outgoing->to;
  1346.             tobufp            = &pf->scratch;
  1347.         break;
  1348.  
  1349.           case N_CC:
  1350.         sending_order[NN+3]    = pf;
  1351.         pf->addr        = &outgoing->cc;
  1352.         break;
  1353.  
  1354.           case N_BCC:
  1355.         sending_order[NN+4]    = pf;
  1356.         pf->addr        = &outgoing->bcc;
  1357.         break;
  1358.  
  1359.           case N_REPLYTO:
  1360.         sending_order[NN+1]    = pf;
  1361.         pf->addr        = &outgoing->reply_to;
  1362.         break;
  1363.  
  1364.           case N_LCC:        /* won't be used here */
  1365.         sending_order[NN+7]    = pf;
  1366.         break;
  1367.  
  1368. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1369.               case N_SENDER:
  1370.         sending_order[3]    = pf;
  1371.         pf->addr        = &outgoing->sender;
  1372.                 break;
  1373. #endif
  1374.  
  1375.               case N_NOBODY: /* won't be used here */
  1376.         sending_order[NN+5]    = pf;
  1377.                 break;
  1378.  
  1379.               default:
  1380.                 q_status_message1(SM_ORDER,3,3,
  1381.             "Internal error: Address header %d", (void *) i);
  1382.                 break;
  1383.             }
  1384.             break;
  1385.  
  1386.           case Fcc:
  1387.         sending_order[NN+8] = pf;
  1388.         ba_fcc.tptr            = NULL;
  1389.         ba_fcc.next            = NULL;
  1390.         pf->text        = &ba_fcc.tptr;
  1391.             break;
  1392.  
  1393.       case Subject:
  1394.         sending_order[NN+6]    = pf;
  1395.         pf->text        = &outgoing->subject;
  1396.         break;
  1397.  
  1398.           default:
  1399.             q_status_message1(SM_ORDER,3,3,
  1400.         "Unknown header type %d in pine_simple_send", (void *)pf->type);
  1401.             break;
  1402.         }
  1403.     }
  1404.  
  1405.     pf->next = NULL;
  1406.  
  1407.     ekey[0].ch    = ctrl('T');
  1408.     ekey[0].rval  = 2;
  1409.     ekey[0].name  = "^T";
  1410.     ekey[0].label = "To AddrBk";
  1411.     ekey[1].ch    = -1;
  1412.  
  1413.     /*----------------------------------------------------------------------
  1414.        Loop editing the "To: " field until everything goes well
  1415.      ----*/
  1416.     help = NO_HELP;
  1417.  
  1418.     while(!done){
  1419.     outgoing2strings(&header, *body, &messagebuf, NULL);
  1420.  
  1421.     resize_len = max(MAXPATH, strlen(*tobufp));
  1422.     fs_resize((void **)tobufp, resize_len+1);
  1423.  
  1424.     if(prompt_for_to)
  1425.       i = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
  1426.                 0, resize_len, 1, 0,
  1427.                 prmpt_who
  1428.                   ? prmpt_who
  1429.                   : outgoing->remail == NULL 
  1430.                     ? "FORWARD (as e-mail) to : "
  1431.                     : "BOUNCE (redirect) message to : ",
  1432.                 ekey, help, 0);
  1433.     else
  1434.       i = 0;
  1435.  
  1436.     switch(i){
  1437.       case -1:
  1438.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  1439.                  "Internal problem encountered");
  1440.         retval = -1;
  1441.         done++;
  1442.         break;
  1443.  
  1444.       case 0:
  1445.         if(**tobufp != '\0'){
  1446.         char *errbuf, *addr, prompt[200];
  1447.         int   tolen, ch;
  1448.  
  1449.         errbuf = NULL;
  1450.         ba_fcc.tptr = NULL;
  1451.         ba_fcc.next = NULL;
  1452.         if(build_address(*tobufp, &addr, &errbuf, &ba_fcc) >= 0){
  1453.             if(errbuf)
  1454.               fs_give((void **)&errbuf);
  1455.  
  1456.             if(strlen(*tobufp) < (tolen = strlen(addr) + 1))
  1457.               fs_resize((void **)tobufp, (size_t) tolen);
  1458.  
  1459.             strcpy(*tobufp, addr);
  1460.             if(used_tobufval)
  1461.               *used_tobufval = cpystr(addr);
  1462.  
  1463.             sprintf(prompt, "%s\"%.50s\"",
  1464.             prmpt_cnf ? prmpt_cnf : "Send message to ",
  1465.             (addr && *addr)
  1466.                 ? addr
  1467.                 : (F_ON(F_FCC_ON_BOUNCE, ps_global)
  1468.                    && ba_fcc.tptr && ba_fcc.tptr[0])
  1469.                 ? ba_fcc.tptr
  1470.                 : "");
  1471.             strings2outgoing(&header, body, NULL);
  1472.             if(addr)
  1473.               fs_give((void **)&addr);
  1474.  
  1475.             if(prompt_for_to && check_addresses(&header))
  1476.               /*--- Addresses didn't check out---*/
  1477.               continue;
  1478.  
  1479.             /* confirm address */
  1480.             if(!prompt_for_to
  1481.              || (ch = want_to(prompt, 'y', 'n', NO_HELP, 0, 0)) == 'y'){
  1482.             char *fcc = NULL;
  1483.             CONTEXT_S *fcc_cntxt = NULL;
  1484.  
  1485.             if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
  1486.                 fcc = ba_fcc.tptr;
  1487.                 set_last_fcc(fcc);
  1488.             }
  1489.  
  1490.             /*---- Check out fcc -----*/
  1491.             if(fcc && *fcc){
  1492.                 local_written = 0;
  1493.                 if(!open_fcc(fcc, &fcc_cntxt, 0)
  1494.            || !(local_so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS))){
  1495.                 /* Open or allocation of fcc failed */
  1496.                 q_status_message(SM_ORDER, 3, 5,
  1497.                      "Message NOT sent or written to fcc.");
  1498.                 dprint(4, (debugfile,"can't open fcc, cont\n"));
  1499.                 if(!prompt_for_to){
  1500.                     retval = -1;
  1501.                     goto finish;
  1502.                 }
  1503.                 else
  1504.                   continue;
  1505.                 }
  1506.             }
  1507.             else
  1508.               local_so = NULL;
  1509.  
  1510.             if(!(outgoing->to || outgoing->cc || outgoing->bcc
  1511.                  || local_so)){
  1512.                 q_status_message(SM_ORDER, 3, 5,
  1513.                 "No recipients specified!");
  1514.                 continue;
  1515.             }
  1516.  
  1517.             if(outgoing->to || outgoing->cc || outgoing->bcc)
  1518.               result = call_mailer(&header, *body);
  1519.             else
  1520.               result = 0;
  1521.  
  1522.             /*----- Was there an fcc involved? -----*/
  1523.             if(local_so){
  1524.                 if(result == 1
  1525.                    || (result == 0
  1526.                && pine_rfc822_output(&header, *body, NULL, NULL))){
  1527.                 char label[50];
  1528.  
  1529.                 strcpy(label, "Fcc");
  1530.                 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC))
  1531.                   sprintf(label + 3, " to %.40s", fcc);
  1532.  
  1533.                 /* Now actually copy to fcc folder and close */
  1534.                 write_fcc(fcc, fcc_cntxt, local_so, label);
  1535.                 }
  1536.                 else if(result == 0){
  1537.                 q_status_message(SM_ORDER,3,5,
  1538.                     "Fcc Failed!.  No message saved.");
  1539.                 retval = -1;
  1540.                 dprint(1,
  1541.                   (debugfile, "explicit fcc write failed!\n"));
  1542.                 }
  1543.  
  1544.                 so_give(&local_so);
  1545.             }
  1546.  
  1547.             if(result < 0){
  1548.                 dprint(1, (debugfile, "Bounce failed\n"));
  1549.                 if(!prompt_for_to)
  1550.                   retval = -1;
  1551.                 else
  1552.                   continue;
  1553.             }
  1554.             }
  1555.             else{
  1556.             q_status_message(SM_ORDER, 0, 3, "Send cancelled");
  1557.             retval = -1;
  1558.             }
  1559.         }
  1560.         else{
  1561.             q_status_message1(SM_ORDER | SM_DING, 3, 5,
  1562.                       "Error in address: %s", errbuf);
  1563.             if(errbuf)
  1564.               fs_give((void **)&errbuf);
  1565.  
  1566.             if(!prompt_for_to)
  1567.               retval = -1;
  1568.             else
  1569.               continue;
  1570.         }
  1571.  
  1572.         }
  1573.         else{
  1574.         q_status_message(SM_ORDER | SM_DING, 3, 5,
  1575.                  "No addressee!  No e-mail sent.");
  1576.         retval = -1;
  1577.         }
  1578.  
  1579.         done++;
  1580.         break;
  1581.  
  1582.       case 1:
  1583.         q_status_message(SM_ORDER, 0, 3, "Send cancelled");
  1584.         done++;
  1585.         retval = -1;
  1586.         break;
  1587.  
  1588.       case 2: /* ^T */
  1589.         {
  1590.         void (*redraw) () = ps_global->redrawer;
  1591.         char  *returned_addr = NULL;
  1592.         int    len;
  1593.  
  1594.         push_titlebar_state();
  1595.         returned_addr = addr_book_bounce();
  1596.         if(returned_addr){
  1597.         if(resize_len < (len = strlen(returned_addr) + 1))
  1598.           fs_resize((void **)tobufp, (size_t)len);
  1599.  
  1600.         strcpy(*tobufp, returned_addr);
  1601.         fs_give((void **)&returned_addr);
  1602.         strings2outgoing(&header, body, NULL);
  1603.         }
  1604.  
  1605.         ClearScreen();
  1606.         pop_titlebar_state();
  1607.         redraw_titlebar();
  1608.         if(ps_global->redrawer = redraw) /* reset old value, and test */
  1609.           (*ps_global->redrawer)();
  1610.         }
  1611.  
  1612.         break;
  1613.  
  1614.       case 3:
  1615.         help = (help == NO_HELP)
  1616.                 ? (outgoing->remail == NULL
  1617.                 ? h_anon_forward
  1618.                 : h_bounce)
  1619.                 : NO_HELP;
  1620.         break;
  1621.  
  1622.       case 4:                /* can't suspend */
  1623.       default:
  1624.         break;
  1625.     }
  1626.     }
  1627.  
  1628. finish:
  1629.     if(ba_fcc.tptr)
  1630.       fs_give((void **)&ba_fcc.tptr);
  1631.  
  1632.     for(i=0; i < cnt; i++)
  1633.       fs_give((void **)&pfields[i].name);
  1634.  
  1635.     fs_give((void **)&pfields);
  1636.     fs_give((void **)&sending_order);
  1637.  
  1638.     return(retval);
  1639. }
  1640.  
  1641.  
  1642. /*----------------------------------------------------------------------
  1643.      Prepare data structures for pico, call pico, then post message
  1644.  
  1645.   Args:  outgoing     -- Partially formatted outgoing ENVELOPE
  1646.          body         -- Body of outgoing message
  1647.          editor_title -- Title for anchor line in composer
  1648.          fcc          -- The file carbon copy field
  1649.      reply_list   -- List of raw c-client message numbers of
  1650.              we're replying to.  This should be GIDs
  1651.              after IMAP4.
  1652.      ref_list     -- News references list of message id's
  1653.      custom -- custom header list. Passed void * so routines outside
  1654.            send.c don't need to know what PINEFIELD is...
  1655.  
  1656.   Result: message is edited, then postponed, cancelled or sent.
  1657.  
  1658.   Fields:
  1659.      remail                -
  1660.      return_path           -
  1661.      date                 added here
  1662.      from                 added here
  1663.      sender                -
  1664.      reply_to              -
  1665.      subject              passed in, edited and cannonized here
  1666.      to                   possibly passed in, edited and cannonized here
  1667.      cc                   possibly passed in, edited and cannonized here
  1668.      bcc                  edited and cannonized here
  1669.      in_reply_to          generated in reply() and passed in
  1670.      message_id            -
  1671.   
  1672.  Storage for these fields comes from anywhere outside. It is remalloced
  1673.  here so the composer can realloc them if needed. The copies here are also 
  1674.  freed here.
  1675.  
  1676. Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
  1677. with the first part TYPETEXT! All newlines in the text here also end with
  1678. CRLF.
  1679.  
  1680. There's a further assumption that the text in the TYPETEXT part is 
  1681. stored in a storage object (see filter.c).
  1682.   ----*/
  1683. void
  1684. pine_send(outgoing, body, editor_title, fcc_arg, reply_list, ref_list,
  1685.       reply_prefix, lcc_arg, custom, sticky_fcc)
  1686.     ENVELOPE  *outgoing;  /* c-client envelope for outgoing message */
  1687.     BODY     **body;   
  1688.     char      *editor_title;
  1689.     char      *fcc_arg;
  1690.     long      *reply_list;
  1691.     char      *ref_list;
  1692.     char      *reply_prefix;
  1693.     char      *lcc_arg;
  1694.     void      *custom;
  1695.     int        sticky_fcc;
  1696. {
  1697.     int            i, fixed_cnt, total_cnt, index,
  1698.             editor_result = 0, body_start = 0;
  1699.     char           *p, *addr, *fcc, *fcc_to_free = NULL;
  1700.     struct headerentry *he, *headents, *he_to, *he_fcc, *he_news, *he_lcc;
  1701.     PINEFIELD          *pfields, *pf, *pf_nobody, *pf_ref, *pf_fcc, *pf_err,
  1702.               **sending_order;
  1703.     METAENV             header;
  1704.     ADDRESS            *lcc_addr = NULL;
  1705.     ADDRESS            *nobody_addr = NULL;
  1706.     STORE_S           *orig_so = NULL;
  1707. #ifdef    DOS
  1708.     char               *reserve;
  1709. #endif
  1710.  
  1711.     dprint(1, (debugfile,"\n=== send called ===\n"));
  1712.  
  1713.     /*
  1714.      * Cancel any pending initial commands since pico uses a different
  1715.      * input routine.  If we didn't cancel them, they would happen after
  1716.      * we returned from the editor, which would be confusing.
  1717.      */
  1718.     if(ps_global->in_init_seq){
  1719.     ps_global->in_init_seq = 0;
  1720.     ps_global->save_in_init_seq = 0;
  1721.     clear_cursor_pos();
  1722.     if(ps_global->initial_cmds){
  1723.         if(ps_global->free_initial_cmds)
  1724.           fs_give((void **)&(ps_global->free_initial_cmds));
  1725.  
  1726.         ps_global->initial_cmds = 0;
  1727.     }
  1728.  
  1729.     F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
  1730.     }
  1731.  
  1732.     if(ps_global->post){
  1733.     q_status_message(SM_ORDER|SM_DING, 3, 3, "Can't post while posting!");
  1734.     return;
  1735.     }
  1736.  
  1737. #if defined(DOS) || defined(OS2)
  1738.     if(!dos_valid_from(1))
  1739.       return;
  1740.  
  1741.     pbuf.upload           = NULL;
  1742. #else
  1743.     pbuf.upload           = (ps_global->VAR_UPLOAD_CMD
  1744.               && ps_global->VAR_UPLOAD_CMD[0])
  1745.               ? upload_msg_to_pico : NULL;
  1746. #endif
  1747.  
  1748.     pbuf.raw_io        = Raw;
  1749.     pbuf.showmsg       = display_message_for_pico;
  1750.     pbuf.newmail       = new_mail_for_pico;
  1751.     pbuf.msgntext      = message_format_for_pico;
  1752.     pbuf.ckptdir       = checkpoint_dir_for_pico;
  1753.     pbuf.mimetype      = mime_type_for_pico;
  1754.     pbuf.exittest      = send_exit_for_pico;
  1755.     pbuf.resize           = resize_for_pico;
  1756.     pbuf.keybinit      = init_keyboard;
  1757.     pbuf.helper        = helper;
  1758.     pbuf.alt_ed        = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0])
  1759.                 ? ps_global->VAR_EDITOR : NULL;
  1760.     pbuf.alt_spell     = ps_global->VAR_SPELLER;
  1761.     pbuf.quote_str     = reply_prefix;
  1762.     pbuf.fillcolumn    = ps_global->composer_fillcol;
  1763.     pbuf.menu_rows     = FOOTER_ROWS(ps_global) - 1;
  1764.     pbuf.ins_help      = h_composer_ins;
  1765.     pbuf.search_help   = h_composer_search;
  1766.     pbuf.browse_help   = h_composer_browse;
  1767.     pbuf.composer_help = h_composer;
  1768.     pbuf.pine_anchor   = set_titlebar(editor_title, ps_global->mail_stream,
  1769.                       ps_global->context_current,
  1770.                       ps_global->cur_folder,ps_global->msgmap, 
  1771.                       0, FolderName, 0, 0);
  1772.     pbuf.pine_version  = pine_version;
  1773.     pbuf.pine_flags    = flags_for_pico(ps_global);
  1774.     if(ps_global->VAR_OPER_DIR){
  1775.     pbuf.oper_dir    = ps_global->VAR_OPER_DIR;
  1776.     pbuf.pine_flags |= P_TREE;
  1777.     }
  1778.  
  1779.     /* NOTE: initial cursor position set below */
  1780.  
  1781.     dprint(9, (debugfile, "flags: %x\n", pbuf.pine_flags));
  1782.  
  1783.     /*
  1784.      * When user runs compose and the current folder is a newsgroup,
  1785.      * offer to post to the current newsgroup.
  1786.      */
  1787.     if(!outgoing->to && !(outgoing->newsgroups && *outgoing->newsgroups)
  1788.        && ps_global->mail_stream && ps_global->mail_stream->mailbox
  1789.        && ps_global->mail_stream->mailbox[0] == '*'){
  1790.     char  prompt[200];
  1791.     char  newsgroup_name[MAILTMPLEN];
  1792.  
  1793.     newsgroup_name[0] = '\0';
  1794.  
  1795.     /* if remote, c-client has a function to parse out mailbox */
  1796.     if(ps_global->mail_stream->mailbox[1] == '{')
  1797.       mail_valid_net(ps_global->mail_stream->mailbox,
  1798.         ps_global->mail_stream->dtb,
  1799.         NULL,
  1800.         newsgroup_name);
  1801.     else
  1802.       (void)strcpy(newsgroup_name, &(ps_global->mail_stream->mailbox[1]));
  1803.  
  1804.     /*
  1805.      * Replies don't get this far because To or Newsgroups will already
  1806.      * be filled in.  So must be either ordinary compose or forward.
  1807.      * Forward sets subject, so use that to tell the difference.
  1808.      */
  1809.     if(newsgroup_name[0] && !outgoing->subject){
  1810.         int ch = 'y';
  1811.         int ret_val;
  1812.         char *errmsg = NULL;
  1813.         BUILDER_ARG     *fcc_build = NULL;
  1814.  
  1815.         if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
  1816.         sprintf(prompt,
  1817.             "Post to current newsgroup (%s)", newsgroup_name);
  1818.         ch = want_to(prompt, 'y', 'x', NO_HELP, 0, 0);
  1819.         }
  1820.  
  1821.         switch(ch){
  1822.           case 'y':
  1823.         if(outgoing->newsgroups)
  1824.           fs_give((void **)&outgoing->newsgroups); 
  1825.  
  1826.         if(!fcc_arg){
  1827.             fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
  1828.             fcc_build->tptr = fcc_to_free;
  1829.             fcc_build->next = NULL;
  1830.             fcc_build->xtra = NULL;
  1831.         }
  1832.  
  1833.         ret_val = news_build(newsgroup_name, &outgoing->newsgroups,
  1834.                      &errmsg, fcc_build);
  1835.  
  1836.         if(ret_val == -1){
  1837.             if(outgoing->newsgroups)
  1838.               fs_give((void **)&outgoing->newsgroups); 
  1839.  
  1840.             outgoing->newsgroups = cpystr(newsgroup_name);
  1841.         }
  1842.  
  1843.         if(!fcc_arg){
  1844.             fcc_arg = fcc_to_free = fcc_build->tptr;
  1845.             fs_give((void **)&fcc_build);
  1846.         }
  1847.  
  1848.         if(errmsg){
  1849.             if(*errmsg){
  1850.             q_status_message(SM_ORDER, 3, 3, errmsg);
  1851.             display_message(NO_OP_COMMAND);
  1852.             }
  1853.  
  1854.             fs_give((void **)&errmsg);
  1855.         }
  1856.  
  1857.         break;
  1858.  
  1859.           case 'x': /* ^C */
  1860.         q_status_message(SM_ORDER, 0, 3, "Message cancelled");
  1861.         dprint(4, (debugfile, "=== send: cancelled\n"));
  1862.         return;
  1863.  
  1864.           case 'n':
  1865.         break;
  1866.  
  1867.           default:
  1868.         break;
  1869.         }
  1870.     }
  1871.     }
  1872.  
  1873.     /* how many fixed fields are there? */
  1874.     fixed_cnt = (sizeof(pf_template)/sizeof(pf_template[0])) - 1;
  1875.  
  1876.     total_cnt = fixed_cnt + count_custom_hdrs();
  1877.  
  1878.     /* the fixed part of the PINEFIELDs */
  1879.     i       = fixed_cnt * sizeof(PINEFIELD);
  1880.     pfields = (PINEFIELD *)fs_get((size_t) i);
  1881.     memset(pfields, 0, (size_t) i);
  1882.  
  1883.     /* temporary headerentry array for pico */
  1884.     i        = (total_cnt + 1) * sizeof(struct headerentry);
  1885.     headents = (struct headerentry *)fs_get((size_t) i);
  1886.     memset(headents, 0, (size_t) i);
  1887.  
  1888.     i             = total_cnt * sizeof(PINEFIELD *);
  1889.     sending_order = (PINEFIELD **)fs_get((size_t) i);
  1890.     memset(sending_order, 0, (size_t) i);
  1891.  
  1892.     pbuf.headents        = headents;
  1893.     header.env           = outgoing;
  1894.     header.local         = pfields;
  1895.     header.sending_order = sending_order;
  1896.  
  1897.     /* custom part of PINEFIELDs */
  1898.     header.custom = (custom) ? (PINEFIELD *)custom : get_dflt_custom_hdrs();
  1899.  
  1900.     he = headents;
  1901.     pf = pfields;
  1902.  
  1903.     /*
  1904.      * For Address types, pf->addr points to an ADDRESS *.
  1905.      * If that address is in the "outgoing" envelope, it will
  1906.      * be freed by the caller, otherwise, it should be freed here.
  1907.      * Pf->textbuf for an Address is used a little to set up a default,
  1908.      * but then is freed right away below.  Pf->scratch is used for a
  1909.      * pointer to some alloced space for pico to edit in.  Addresses in
  1910.      * the custom area are freed by free_customs().
  1911.      *
  1912.      * For FreeText types, pf->addr is not used.  Pf->text points to a
  1913.      * pointer that points to the text.  Pf->textbuf points to a copy of
  1914.      * the text that must be freed before we leave, otherwise, it is
  1915.      * probably a pointer into the envelope and that gets freed by the
  1916.      * caller.
  1917.      *
  1918.      * He->realaddr is the pointer to the text that pico actually edits.
  1919.      */
  1920.  
  1921. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1922. #define NN 3
  1923. #else
  1924. #define NN 2
  1925. #endif
  1926.  
  1927.     /* initialize the fixed header elements of the two temp arrays */
  1928.     for(i=0; i < fixed_cnt; i++, pf++){
  1929.  
  1930.     /* slightly different editing order if sending to news */
  1931.     if(outgoing->newsgroups && *outgoing->newsgroups){
  1932.         index = (i == 0) ? N_FROM :
  1933.              (i == 1) ? N_REPLYTO :
  1934.               (i == 2) ? N_NEWS :
  1935.                (i == 3) ? N_TO :
  1936.             (i == 4) ? N_CC :
  1937.              (i == 5) ? N_BCC :
  1938.               (i == 6) ? N_FCC :
  1939.                (i == 7) ? N_LCC :
  1940.                 (i == 8) ? N_ATTCH :
  1941.                  (i == 9) ? N_SUBJ :
  1942.                   (i == 10) ? N_REF :
  1943.                    (i == 11) ? N_DATE :
  1944.                 (i == 12) ? N_INREPLY :
  1945.                  (i == 13) ? N_MSGID :
  1946.                   (i == 14) ? N_NOBODY :
  1947.                    (i == 15) ? N_POSTERR :
  1948. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1949.                     (i == 16) ? N_SENDER :
  1950. #endif
  1951.                               i;
  1952.     }
  1953.     else
  1954.       index = i;
  1955.  
  1956.     if(i > 16)
  1957.       q_status_message1(SM_ORDER,3,7,
  1958.           "Internal error: i=%d in pine_send", (void *)i);
  1959.  
  1960.     /* copy the templates */
  1961.     *he             = he_template[index];
  1962.  
  1963.     pf->name        = cpystr(pf_template[index].name);
  1964.     if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
  1965.       /* slide string over so it is Sender instead of X-Sender */
  1966.       for(p=pf->name; *(p+1); p++)
  1967.         *p = *(p+2);
  1968.  
  1969.     pf->type        = pf_template[index].type;
  1970.     pf->canedit     = pf_template[index].canedit;
  1971.     pf->rcptto      = pf_template[index].rcptto;
  1972.     pf->writehdr    = pf_template[index].writehdr;
  1973.     pf->localcopy   = pf_template[index].localcopy;
  1974.     pf->he          = he;
  1975.     pf->next        = pf + 1;
  1976.  
  1977.     he->rich_header = view_as_rich(pf->name, he->rich_header);
  1978.  
  1979.     switch(pf->type){
  1980.       case FreeText:           /* realaddr points to c-client env */
  1981.         if(index == N_NEWS){
  1982.         sending_order[0]    = pf;
  1983.         he->realaddr        = &outgoing->newsgroups;
  1984.         /* If there is a newsgrp already, we'd better show them */
  1985.         if(outgoing->newsgroups && *outgoing->newsgroups)
  1986.           he->rich_header = 0; /* force on by default */
  1987.  
  1988.             he_news            = he;
  1989.  
  1990.         /*
  1991.          * If this field doesn't already have a value, then we want
  1992.          * to check for a default value assigned by the user.  If no
  1993.          * default, give it an alloced empty string.
  1994.          */
  1995.         if(!*he->realaddr){        /* no value */
  1996.             set_default_hdrval(pf);    /* default in pf->textbuf */
  1997.             *he->realaddr = pf->textbuf;
  1998.             pf->textbuf   = NULL;
  1999.         }
  2000.  
  2001.         pf->text = he->realaddr;
  2002.         }
  2003.         else if(index == N_DATE){
  2004.         sending_order[1]    = pf;
  2005.         pf->text        = &outgoing->date;
  2006.         pf->he            = NULL;
  2007.         }
  2008.         else if(index == N_INREPLY){
  2009.         sending_order[NN+9]    = pf;
  2010.         pf->text        = &outgoing->in_reply_to;
  2011.         pf->he            = NULL;
  2012.         }
  2013.         else if(index == N_MSGID){
  2014.         sending_order[NN+10]    = pf;
  2015.         pf->text        = &outgoing->message_id;
  2016.         pf->he            = NULL;
  2017.         }
  2018.         else if(index == N_REF){
  2019.         sending_order[NN+11]    = pf;
  2020.         pf_ref            = pf;
  2021.         if(ref_list)
  2022.           pf->textbuf = cpystr(ref_list);
  2023.  
  2024.         pf->text        = &pf->textbuf;
  2025.         pf->he            = NULL;
  2026.         }
  2027.         else if(index == N_POSTERR){
  2028.         sending_order[NN+12]    = pf;
  2029.         pf_err            = pf;
  2030.         pf->text        = &pf->textbuf;
  2031.         pf->he            = NULL;
  2032.         }
  2033.         else{
  2034.         q_status_message(SM_ORDER | SM_DING, 3, 7,
  2035.                 "Botched: Unmatched FreeText header in pine_send");
  2036.         }
  2037.  
  2038.         break;
  2039.  
  2040.       /* can't do a default for this one */
  2041.       case Attachment:
  2042.         /* If there is an attachment already, we'd better show them */
  2043.             if(body && *body && (*body)->type != TYPETEXT)
  2044.           he->rich_header = 0; /* force on by default */
  2045.  
  2046.         break;
  2047.  
  2048.       case Address:
  2049.         switch(index){
  2050.           case N_FROM:
  2051.         sending_order[2]    = pf;
  2052.         pf->addr        = &outgoing->from;
  2053.         break;
  2054.  
  2055.           case N_TO:
  2056.         sending_order[NN+2]    = pf;
  2057.         pf->addr        = &outgoing->to;
  2058.         /* If already set, make it act like we typed it in */
  2059.         if(outgoing->to
  2060.            && outgoing->to->mailbox
  2061.            && outgoing->to->mailbox[0])
  2062.           he->sticky = 1;
  2063.  
  2064.         he_to            = he;
  2065.         break;
  2066.  
  2067.           case N_NOBODY:
  2068.         sending_order[NN+5]    = pf;
  2069.         pf_nobody        = pf;
  2070.         if(ps_global->VAR_EMPTY_HDR_MSG
  2071.            && !ps_global->VAR_EMPTY_HDR_MSG[0]){
  2072.             pf->addr        = NULL;
  2073.         }
  2074.         else{
  2075.             nobody_addr          = mail_newaddr();
  2076.             nobody_addr->next    = mail_newaddr();
  2077.             nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
  2078.                 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
  2079.                         ? ps_global->VAR_EMPTY_HDR_MSG
  2080.                         : "Undisclosed recipients"),
  2081.                 ps_global->VAR_CHAR_SET));
  2082.             pf->addr        = &nobody_addr;
  2083.         }
  2084.  
  2085.         break;
  2086.  
  2087.           case N_CC:
  2088.         sending_order[NN+3]    = pf;
  2089.         pf->addr        = &outgoing->cc;
  2090.         break;
  2091.  
  2092.           case N_BCC:
  2093.         sending_order[NN+4]    = pf;
  2094.         pf->addr        = &outgoing->bcc;
  2095.         /* if bcc exists, make sure it's exposed so nothing's
  2096.          * sent by mistake...
  2097.          */
  2098.         if(outgoing->bcc)
  2099.           he->display_it = 1;
  2100.  
  2101.         break;
  2102.  
  2103.           case N_REPLYTO:
  2104.         sending_order[NN+1]    = pf;
  2105.         pf->addr        = &outgoing->reply_to;
  2106.         break;
  2107.  
  2108.           case N_LCC:
  2109.         sending_order[NN+7]    = pf;
  2110.         pf->addr        = &lcc_addr;
  2111.         he_lcc            = he;
  2112.         if(lcc_arg){
  2113.             build_address(lcc_arg, &addr, NULL, NULL);
  2114.             rfc822_parse_adrlist(&lcc_addr, addr,
  2115.             ps_global->maildomain);
  2116.             fs_give((void **)&addr);
  2117.             he->display_it = 1;
  2118.         }
  2119.  
  2120.         break;
  2121.  
  2122. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  2123.               case N_SENDER:
  2124.         sending_order[3]    = pf;
  2125.         pf->addr        = &outgoing->sender;
  2126.                 break;
  2127. #endif
  2128.  
  2129.           default:
  2130.         q_status_message1(SM_ORDER,3,7,
  2131.             "Internal error: Address header %d", (void *)index);
  2132.         break;
  2133.         }
  2134.  
  2135.         /*
  2136.          * If this is a reply to news, don't show the regular email
  2137.          * recipient headers (unless they are non-empty).
  2138.          */
  2139.         if((outgoing->newsgroups && *outgoing->newsgroups)
  2140.            && (index == N_TO || index == N_CC
  2141.            || index == N_BCC || index == N_LCC)
  2142.            && (!*pf->addr)){
  2143.         he->rich_header = 1; /* hide */
  2144.         }
  2145.  
  2146.         /*
  2147.          * If this address doesn't already have a value, then we check
  2148.          * for a default value assigned by the user.
  2149.          */
  2150.         else if(pf->addr && !*pf->addr){
  2151.         set_default_hdrval(pf);
  2152.  
  2153. #ifndef ALLOW_CHANGING_FROM
  2154.         if(index != N_FROM){  /* don't set default for From */
  2155. #endif
  2156.             if(pf->textbuf && *pf->textbuf){
  2157.                 /* strip quotes around whole default */
  2158.                 if(*pf->textbuf == '"'){
  2159.                   char *p;
  2160.  
  2161.                   /* strip trailing whitespace */
  2162.                   p = pf->textbuf + strlen(pf->textbuf) - 1;
  2163.                   while(isspace((unsigned char)*p) && p > pf->textbuf)
  2164.                 p--;
  2165.  
  2166.                   /* a set of surrounding quotes we want to strip */
  2167.                   if(*p == '"'){
  2168.                 *p = '\0';
  2169.                 for(p=pf->textbuf; *(p+1); p++)
  2170.                   *p = *(p+1);
  2171.  
  2172.                 *p = '\0';
  2173.                   }
  2174.                 }
  2175.  
  2176.                 build_address(pf->textbuf, &addr, NULL, NULL);
  2177.                 rfc822_parse_adrlist(pf->addr,
  2178.                 addr, ps_global->maildomain);
  2179.                 fs_give((void **)&addr);
  2180.             }
  2181. #ifndef ALLOW_CHANGING_FROM
  2182.         }
  2183. #endif
  2184.  
  2185.         /* if we still don't have a from */
  2186.         if(index == N_FROM && !*pf->addr)
  2187.           *pf->addr = generate_from();
  2188.  
  2189.         }else if(index == N_FROM || index == N_REPLYTO){
  2190.         /* side effect of this may set canedit */
  2191.         set_default_hdrval(pf);
  2192.         }else if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
  2193.         fs_give((void **)pf->addr);
  2194.         he->display_it = 1;  /* start this off showing */
  2195.         }
  2196.         else
  2197.           he->display_it = 1;  /* start this off showing */
  2198.  
  2199.         if(!outgoing->return_path)
  2200.           outgoing->return_path = rfc822_cpy_adr(outgoing->from);
  2201.  
  2202.         if(pf->textbuf)        /* free default value in any case */
  2203.           fs_give((void **)&pf->textbuf);
  2204.  
  2205.         /* outgoing2strings will alloc the string pf->scratch below */
  2206.         he->realaddr = &pf->scratch;
  2207.         break;
  2208.  
  2209.       case Fcc:
  2210.         sending_order[NN+8] = pf;
  2211.         pf_fcc              = pf;
  2212.         fcc                 = get_fcc(fcc_arg);
  2213.         if(fcc_to_free){
  2214.         fs_give((void **)&fcc_to_free);
  2215.         fcc_arg = NULL;
  2216.         }
  2217.  
  2218.         if(sticky_fcc && fcc[0])
  2219.           he->sticky = 1;
  2220.  
  2221.         if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
  2222.           he->display_it = 1;  /* start this off showing */
  2223.  
  2224.         he->realaddr  = &fcc;
  2225.         pf->text      = &fcc;
  2226.         he_fcc        = he;
  2227.         break;
  2228.  
  2229.       case Subject :
  2230.         sending_order[NN+6]    = pf;
  2231.         if(outgoing->subject){
  2232.         pf->scratch = cpystr(outgoing->subject);
  2233.         }
  2234.         else{
  2235.         set_default_hdrval(pf);    /* default in pf->textbuf */
  2236.         pf->scratch = pf->textbuf;
  2237.         pf->textbuf   = NULL;
  2238.         }
  2239.  
  2240.         he->realaddr = &pf->scratch;
  2241.         pf->text     = &outgoing->subject;
  2242.         break;
  2243.  
  2244.       default:
  2245.         q_status_message1(SM_ORDER,3,7,
  2246.                   "Unknown header type %d in pine_send",
  2247.                   (void *)pf->type);
  2248.         break;
  2249.     }
  2250.  
  2251.     /*
  2252.      * We may or may not want to give the user the chance to edit
  2253.      * the From and Reply-To lines.  If they are listed in either
  2254.      * Default-composer-hdrs or Customized-hdrs, then they can edit
  2255.      * them, else no.
  2256.      * If canedit is not set, that means that this header is not in
  2257.      * the user's customized-hdrs.  If rich_header is set, that
  2258.      * means that this header is not in the user's
  2259.      * default-composer-hdrs (since From and Reply-To are rich
  2260.      * by default).  So, don't give it an he to edit with in that case.
  2261.      *
  2262.      * For other types, just not setting canedit will cause it to be
  2263.      * uneditable, regardless of what the user does.
  2264.      */
  2265.     switch(index){
  2266.       case N_FROM:
  2267. /* to allow it, we let this fall through to the reply-to case below */
  2268. #ifndef ALLOW_CHANGING_FROM
  2269.         if(pf->canedit || !he->rich_header)
  2270.           q_status_message(SM_ORDER, 3, 3,
  2271.             "Not allowed to change header \"From\"");
  2272.  
  2273.         memset(he, 0, (size_t)sizeof(*he));
  2274.         pf->he = NULL;
  2275.         break;
  2276. #endif
  2277.  
  2278.       case N_REPLYTO:
  2279.         if(!pf->canedit && he->rich_header){
  2280.             memset(he, 0, (size_t)sizeof(*he));
  2281.         pf->he = NULL;
  2282.         }
  2283.         else{
  2284.         pf->canedit = 1;
  2285.         he++;
  2286.         }
  2287.  
  2288.         break;
  2289.  
  2290.       default:
  2291.         if(!pf->canedit){
  2292.             memset(he, 0, (size_t)sizeof(*he));
  2293.         pf->he = NULL;
  2294.         }
  2295.         else
  2296.           he++;
  2297.  
  2298.         break;
  2299.     }
  2300.     }
  2301.  
  2302.     /*
  2303.      * This is so the builder can tell the composer to fill the affected
  2304.      * field based on the value in the field on the left.
  2305.      *
  2306.      * Note that this mechanism isn't completely general.  Each entry has
  2307.      * only a single next_affected, so if some other entry points an
  2308.      * affected entry at an entry with a next_affected, they all inherit
  2309.      * that next_affected.  Since this isn't used much a careful ordering
  2310.      * of the affected fields should make it a sufficient mechanism.
  2311.      */
  2312.     he_to->affected_entry   = he_fcc;
  2313.     he_news->affected_entry = he_fcc;
  2314.     he_lcc->affected_entry  = he_to;
  2315.     he_to->next_affected    = he_fcc;
  2316.  
  2317.     (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
  2318.  
  2319.     i--;  /* subtract one because N_ATTCH doesn't get a sending_order slot */
  2320.     /*
  2321.      * Set up headerentries for custom fields.
  2322.      * NOTE: "i" is assumed to now index first custom field in sending
  2323.      *       order.
  2324.      */
  2325.     for(pf = pf->next; pf && pf->name; pf = pf->next, he++){
  2326.     char *addr;
  2327.  
  2328.     pf->he          = he;
  2329.     pf->canedit     = 1;
  2330.     pf->rcptto      = 0;
  2331.     pf->writehdr    = 1;
  2332.     pf->localcopy   = 1;
  2333.     
  2334.     switch(pf->type){
  2335.       case Address:
  2336.         sending_order[i++] = pf;
  2337.         *he = he_custom_addr_templ;
  2338.         /* change default text into an ADDRESS */
  2339.         /* strip quotes around whole default */
  2340.         if(*pf->textbuf == '"'){
  2341.           char *p;
  2342.  
  2343.           /* strip trailing whitespace */
  2344.           p = pf->textbuf + strlen(pf->textbuf) - 1;
  2345.           while(isspace((unsigned char)*p) && p > pf->textbuf)
  2346.         p--;
  2347.  
  2348.           /* this is a set of surrounding quotes we want to strip */
  2349.           if(*p == '"'){
  2350.         *p = '\0';
  2351.         for(p=pf->textbuf; *(p+1); p++)
  2352.           *p = *(p+1);
  2353.  
  2354.         *p = '\0';
  2355.           }
  2356.         }
  2357.  
  2358.         build_address(pf->textbuf, &addr, NULL, NULL);
  2359.         rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
  2360.         fs_give((void **)&addr);
  2361.         if(pf->textbuf)
  2362.           fs_give((void **)&pf->textbuf);
  2363.  
  2364.         he->realaddr = &pf->scratch;
  2365.         break;
  2366.  
  2367.       case FreeText:
  2368.         sending_order[i++] = pf;
  2369.         *he                = he_custom_free_templ;
  2370.         he->realaddr       = &pf->textbuf;
  2371.         pf->text           = &pf->textbuf;
  2372.         break;
  2373.  
  2374.       default:
  2375.         q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
  2376.                             (void *)pf->type);
  2377.         break;
  2378.     }
  2379.  
  2380.     he->name = pf->name;
  2381.  
  2382.     /* use first 8 characters for prompt */
  2383.     he->prompt = cpystr("        : ");
  2384.     strncpy(he->prompt, he->name, min(strlen(he->name), he->prlen - 2));
  2385.  
  2386.     he->rich_header = view_as_rich(he->name, he->rich_header);
  2387.     }
  2388.  
  2389.     /*
  2390.      * Make sure at least *one* field is displayable...
  2391.      */
  2392.     for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
  2393.       if(pf->he && !pf->he->rich_header){
  2394.       index = i;
  2395.       break;
  2396.       }
  2397.  
  2398.     /*
  2399.      * None displayable!!!  Warn and display defaults.
  2400.      */
  2401.     if(index == -1){
  2402.     q_status_message(SM_ORDER,0,5,
  2403.              "No default-composer-hdrs matched, displaying defaults");
  2404.     for(i = 0, pf = header.local; pf; pf = pf->next, i++)
  2405.       if(i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
  2406.         pf->he->rich_header = 0;
  2407.     }
  2408.  
  2409.     /*----------------------------------------------------------------------
  2410.        Loop calling the editor until everything goes well
  2411.      ----*/
  2412.     while(1){
  2413.     /*
  2414.      * set initial cursor position based on how many times we've been
  2415.      * thru the loop...
  2416.      */
  2417.     if((reply_list && reply_list[0] != -1) || body_start){
  2418.         pbuf.pine_flags |= P_BODY;
  2419.         body_start = 0;        /* maybe not next time */
  2420.     }
  2421.     else if(reply_list)
  2422.       pbuf.pine_flags |= P_HEADEND;
  2423.  
  2424.     /* in case these were turned on in previous pass through loop */
  2425.     if(pf_ref){
  2426.         pf_ref->writehdr     = 0;
  2427.         pf_ref->localcopy    = 0;
  2428.     }
  2429.  
  2430.     if(pf_nobody){
  2431.         pf_nobody->writehdr  = 0;
  2432.         pf_nobody->localcopy = 0;
  2433.     }
  2434.  
  2435.     if(pf_fcc)
  2436.       pf_fcc->localcopy = 0;
  2437.  
  2438.     /*
  2439.      * If a sending attempt failed after we passed the message text
  2440.      * thru a user-defined filter, "orig_so" points to the original
  2441.      * text.  Replace the body's encoded data with the original...
  2442.      */
  2443.     if(orig_so){
  2444.         STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
  2445.                 ? &(*body)->contents.part->body.contents.binary
  2446.                 : &(*body)->contents.binary);
  2447.         so_give(so);
  2448.         *so     = orig_so;
  2449.         orig_so = NULL;
  2450.     }
  2451.  
  2452.         /*
  2453.          * Convert the envelope and body to the string format that
  2454.          * pico can edit
  2455.          */
  2456.         outgoing2strings(&header, *body, &pbuf.msgtext, &pbuf.attachments);
  2457.  
  2458.     for(pf = header.local; pf && pf->name; pf = pf->next){
  2459.         /*
  2460.          * If this isn't the first time through this loop, we may have
  2461.          * freed some of the FreeText headers below so that they wouldn't
  2462.          * show up as empty headers in the finished message.  Need to
  2463.          * alloc them again here so they can be edited.
  2464.          */
  2465.         if(pf->type == FreeText && pf->he && !*pf->he->realaddr)
  2466.           *pf->he->realaddr = cpystr("");
  2467.  
  2468.         if(pf->type != Attachment && pf->he && *pf->he->realaddr)
  2469.           pf->he->maxlen = strlen(*pf->he->realaddr);
  2470.     }
  2471.  
  2472. #ifdef _WINDOWS
  2473.     mswin_setwindowmenu (MENU_COMPOSER);
  2474. #endif
  2475. #if    defined(DOS) && !defined(_WINDOWS)
  2476. /*
  2477.  * dumb hack to help ensure we've got something left
  2478.  * to send with if the user runs out of precious memory
  2479.  * in the composer...    (FIX THIS!!!)
  2480.  */
  2481.     if((reserve = (char *)malloc(16384)) == NULL)
  2482.       q_status_message(SM_ORDER | SM_DING, 0, 5,
  2483.                "LOW MEMORY!  May be unable to complete send!");
  2484. #endif
  2485.  
  2486.     cancel_busy_alarm(-1);
  2487.         flush_status_messages(1);
  2488.  
  2489.     dprint(1, (debugfile, "\n  ---- COMPOSER ----\n"));
  2490.     editor_result = pico(&pbuf);
  2491.     dprint(4, (debugfile, "... composer returns (0x%x)\n", editor_result));
  2492.  
  2493. #if    defined(DOS) && !defined(_WINDOWS)
  2494.     free((char *)reserve);
  2495. #endif
  2496. #ifdef _WINDOWS
  2497.     mswin_setwindowmenu (MENU_DEFAULT);
  2498. #endif
  2499.     fix_windsize(ps_global);
  2500.     /*
  2501.      * Only reinitialize signals if we didn't receive an interesting
  2502.      * one while in pico, since pico's return is part of processing that
  2503.      * signal and it should continue to be ignored.
  2504.      */
  2505.     if(!(editor_result & COMP_GOTHUP))
  2506.       init_signals();        /* Pico has it's own signal stuff */
  2507.  
  2508.     /*
  2509.      * We're going to save in DEADLETTER.  Dump attachments first.
  2510.      */
  2511.     if(editor_result & COMP_CANCEL)
  2512.       free_attachment_list(&pbuf.attachments);
  2513.  
  2514.         /* Turn strings back into structures */
  2515.         strings2outgoing(&header, body, pbuf.attachments);
  2516.   
  2517.         /* Make newsgroups NULL if it is "" (so won't show up in headers) */
  2518.     if(outgoing->newsgroups){
  2519.         sqzspaces(outgoing->newsgroups);
  2520.         if(!outgoing->newsgroups[0])
  2521.           fs_give((void **)&(outgoing->newsgroups));
  2522.     }
  2523.  
  2524.         /* Make subject NULL if it is "" (so won't show up in headers) */
  2525.         if(outgoing->subject && !outgoing->subject[0])
  2526.           fs_give((void **)&(outgoing->subject)); 
  2527.     
  2528.     /* remove custom fields that are empty */
  2529.     for(pf = header.local; pf && pf->name; pf = pf->next){
  2530.       if(pf->type == FreeText && pf->textbuf){
  2531.         if(pf->textbuf[0] == '\0'){
  2532.         fs_give((void **)&pf->textbuf); 
  2533.         pf->text = NULL;
  2534.         }
  2535.       }
  2536.     }
  2537.  
  2538.         removing_trailing_white_space(fcc);
  2539.  
  2540.     /*-------- Stamp it with a current date -------*/
  2541.     if(outgoing->date)            /* update old date */
  2542.       fs_give((void **)&(outgoing->date));
  2543.  
  2544.     rfc822_date(tmp_20k_buf);        /* format and copy new date */
  2545.     outgoing->date = cpystr(tmp_20k_buf);
  2546.  
  2547. #ifdef OLDWAY
  2548. /* some people objected to our doing this. */
  2549.     /*------- If Reply-To same as From, get rid of it -------*/
  2550.     if(outgoing->reply_to
  2551.        && address_is_same(outgoing->from, outgoing->reply_to))
  2552.       mail_free_address(&outgoing->reply_to);
  2553. #endif /* OLDWAY */
  2554.  
  2555.     /*
  2556.      * Don't ever believe the sender that is there.
  2557.      * If From doesn't look quite right, generate our own sender.
  2558.      */
  2559.     if(outgoing->sender)
  2560.       mail_free_address(&outgoing->sender);
  2561.  
  2562.     /*
  2563.      * If the LHS of the address doesn't match, or the RHS
  2564.      * doesn't match one of localdomain or hostname,
  2565.      * then add a sender line (really X-Sender).
  2566.      *
  2567.      * Don't add a personal_name since the user can change that.
  2568.      */
  2569.     if(!outgoing->from
  2570.        || !outgoing->from->mailbox
  2571.        || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
  2572.        || !outgoing->from->host
  2573.        || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
  2574.        || strucmp(outgoing->from->host, ps_global->hostname) == 0)){
  2575.  
  2576.         outgoing->sender          = mail_newaddr();
  2577.         outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
  2578.         outgoing->sender->host    = cpystr(ps_global->hostname);
  2579.     }
  2580.  
  2581.         /*----- Message is edited, now decide what to do with it ----*/
  2582.     if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
  2583.             /*=========== Postpone or Interrupted message ============*/
  2584.         CONTEXT_S *fcc_cntxt = NULL;
  2585.         char       folder[MAXPATH+1];
  2586.         int           fcc_result = 0;
  2587.         char       label[50];
  2588.  
  2589.         dprint(4, (debugfile, "pine_send:%s handling\n",
  2590.                (editor_result & COMP_SUSPEND)
  2591.                ? "SUSPEND"
  2592.                : (editor_result & COMP_GOTHUP)
  2593.                    ? "HUP"
  2594.                    : (editor_result & COMP_CANCEL)
  2595.                    ? "CANCEL" : "HUH?"));
  2596.         if((editor_result & COMP_CANCEL)
  2597.            && F_ON(F_QUELL_DEAD_LETTER, ps_global)){
  2598.         q_status_message(SM_ORDER, 0, 3, "Message cancelled");
  2599.         break;
  2600.         }
  2601.  
  2602.         /*
  2603.          * The idea here is to use the Fcc: writing facility
  2604.          * to append to the special postponed message folder...
  2605.          *
  2606.          * NOTE: the strategy now is to write the message and
  2607.          * all attachments as they exist at composition time.
  2608.          * In other words, attachments are postponed by value
  2609.          * and not reference.  This may change later, but we'll
  2610.          * need a local "message/external-body" type that
  2611.          * outgoing2strings knows how to properly set up for
  2612.          * the composer.  Maybe later...
  2613.          */
  2614.  
  2615.         label[0] = '\0';
  2616.  
  2617.         if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
  2618.            && (editor_result & COMP_SUSPEND)
  2619.            && check_addresses(&header)){
  2620.         /*--- Addresses didn't check out---*/
  2621.         q_status_message(SM_ORDER, 7, 7,
  2622.           "Not allowed to postpone message until addresses are qualified");
  2623.         continue;
  2624.             }
  2625.  
  2626.         /*
  2627.          * Build the local_so.  In the HUP case, we'll write the
  2628.          * bezerk delimiter by hand and output the message directly
  2629.          * into the folder.  It's not only faster, we don't have to
  2630.          * worry about c-client reentrance and less hands paw over
  2631.          * over the data so there's less chance of a problem.
  2632.          *
  2633.          * In the Postpone case, just create it if the user wants to
  2634.          * and create a temporary storage object to write into.
  2635.          */
  2636.   fake_hup:
  2637.         local_written = 0;
  2638.         if(editor_result & COMP_GOTHUP){
  2639.         int    newfile;
  2640.         time_t now = time((time_t *)0);
  2641.  
  2642.         build_path(folder,
  2643.               ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
  2644.                           : ps_global->home_dir,
  2645.               INTERRUPTED_MAIL);
  2646.         newfile = can_access(folder, ACCESS_EXISTS);
  2647.         if(local_so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)){
  2648.             sprintf(tmp_20k_buf, "%sFrom %s@%s %.24s\015\012",
  2649.                 newfile ? "" : "\015\012",
  2650.                 outgoing->from->mailbox,
  2651.                 outgoing->from->host, ctime(&now));
  2652.             if(!so_puts(local_so, tmp_20k_buf))
  2653.               dprint(1, (debugfile,"***CAN'T WRITE %s: %s\n",
  2654.                  folder, error_description(errno)));
  2655.         }
  2656.         }
  2657.         else if(editor_result & COMP_SUSPEND){
  2658.         if(!ps_global->VAR_POSTPONED_FOLDER
  2659.            || !ps_global->VAR_POSTPONED_FOLDER[0]){
  2660.             q_status_message(SM_ORDER, 3, 3,
  2661.                      "No postponed file defined");
  2662.             continue;
  2663.         }
  2664.  
  2665.         strcpy(folder, ps_global->VAR_POSTPONED_FOLDER);
  2666.         strcpy(label, "postponed message");
  2667.         local_so = open_fcc(folder,&fcc_cntxt, 1)
  2668.               ? so_get(FCC_SOURCE, NULL, WRITE_ACCESS) : NULL;
  2669.         }
  2670.         else{                /* canceled message */
  2671. #if defined(DOS) || defined(OS2)
  2672.         /*
  2673.          * we can't assume anything about root or home dirs, so
  2674.          * just plunk it down in the same place as the pinerc
  2675.          */
  2676.         if(!getenv("HOME")){
  2677.             char *lc = last_cmpnt(ps_global->pinerc);
  2678.             folder[0] = '\0';
  2679.             if(lc != NULL){
  2680.             strncpy(folder,ps_global->pinerc,lc-ps_global->pinerc);
  2681.             folder[lc - ps_global->pinerc] = '\0';
  2682.             }
  2683.  
  2684.             strcat(folder, DEADLETTER);
  2685.         }
  2686.         else
  2687. #endif
  2688.         build_path(folder,
  2689.             ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
  2690.                     : ps_global->home_dir, DEADLETTER);
  2691.  
  2692.         strcpy(label, DEADLETTER);
  2693.         unlink(folder);
  2694.         local_so = open_fcc(folder,&fcc_cntxt, 1)
  2695.               ? so_get(FCC_SOURCE, NULL, WRITE_ACCESS) : NULL;
  2696.         }
  2697.  
  2698.         if(local_so){
  2699.  
  2700.         /* Turn on references header */
  2701.         if(ref_list){
  2702.             pf_ref->writehdr  = 1;
  2703.             pf_ref->localcopy = 1;
  2704.         }
  2705.  
  2706.         /* copy fcc line to postponed or interrupted folder */
  2707.             if(pf_fcc)
  2708.           pf_fcc->localcopy = 1;
  2709.  
  2710.         if((editor_result & ~0xff) && last_message_queued()){
  2711.             pf_err->writehdr  = 1;
  2712.             pf_err->localcopy = 1;
  2713.             pf_err->textbuf   = cpystr(last_message_queued());
  2714.         }
  2715.  
  2716.         /*
  2717.          * We need to make sure any header values that got cleared
  2718.          * get written to the postponed message (they won't if
  2719.          * pf->text is NULL).  Otherwise, we can't tell previously
  2720.          * non-existent custom headers or default values from 
  2721.          * custom (or other) headers that got blanked in the
  2722.          * composer...
  2723.          */
  2724.         for(pf = header.local; pf && pf->name; pf = pf->next)
  2725.           if(pf->type == FreeText && pf->he && !*(pf->he->realaddr))
  2726.             *(pf->he->realaddr) = cpystr("");
  2727.  
  2728.         if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0){
  2729.             if(editor_result & COMP_GOTHUP){
  2730.             char    *err;
  2731.             STORE_S *hup_so;
  2732.             gf_io_t     gc, pc;
  2733.  
  2734.             if(hup_so = so_get(FileStar, folder, WRITE_ACCESS)){
  2735.                 gf_set_so_readc(&gc, local_so);
  2736.                 gf_set_so_writec(&pc, hup_so);
  2737.                 so_seek(local_so, 0L, 0);     /* read msg copy and */
  2738.                 so_seek(hup_so, 0L, 2);    /* append to folder  */
  2739.                 gf_filter_init();
  2740.                 gf_link_filter(gf_nvtnl_local);
  2741.                 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
  2742.                   dprint(1, (debugfile, "*** PIPE FAILED: %s\n",
  2743.                      err));
  2744.  
  2745.                 so_give(&hup_so);
  2746.             }
  2747.             else
  2748.               dprint(1, (debugfile, "*** CAN'T CREATE %s: %s\n",
  2749.                      folder, error_description(errno)));
  2750.             }
  2751.             else
  2752.               fcc_result = write_fcc(folder,fcc_cntxt,local_so,label);
  2753.         }
  2754.  
  2755.         so_give(&local_so);
  2756.         }
  2757.         else
  2758.           dprint(1, (debugfile, "***CAN'T ALLOCATE temp store: %s ",
  2759.              error_description(errno)));
  2760.  
  2761.         if(editor_result & COMP_GOTHUP){
  2762.         /*
  2763.          * Special Hack #291: if any hi-byte bits are set in
  2764.          *              editor's result, we put them there.
  2765.          */
  2766.         if(editor_result & 0xff00)
  2767.           exit(editor_result >> 8);
  2768.  
  2769.         dprint(1, (debugfile, "Save composition on HUP %sED\n",
  2770.                fcc_result ? "SUCCEED" : "FAIL"));
  2771.         hup_signal();        /* Do what we normally do on SIGHUP */
  2772.         }
  2773.         else if(editor_result & COMP_SUSPEND && fcc_result){
  2774.         q_status_message(SM_ORDER, 0, 3,
  2775.              "Composition postponed. Select Compose to resume.");
  2776.                 break; /* postpone went OK, get out of here */
  2777.         }
  2778.         else if(editor_result & COMP_CANCEL){
  2779.         q_status_message(SM_ORDER, 0, 3, "Message cancelled");
  2780.         break;
  2781.             }
  2782.         else{
  2783.         q_status_message(SM_ORDER, 0, 4,
  2784.             "Continuing composition.  Message not postponed or sent");
  2785.         body_start = 1;
  2786.         continue; /* postpone failed, jump back in to composer */
  2787.             }
  2788.     }
  2789.     else{
  2790.         /*------ Must be sending mail or posting ! -----*/
  2791.         int           result;
  2792.         CONTEXT_S *fcc_cntxt = NULL;
  2793.  
  2794.         result = 0;
  2795.         dprint(4, (debugfile, "=== sending: "));
  2796.  
  2797.             /* --- If posting, confirm with user ----*/
  2798.         if(outgoing->newsgroups && *outgoing->newsgroups
  2799.            && want_to(POST_PMT, 'n', 'n', NO_HELP, 0, 0) == 'n'){
  2800.         q_status_message(SM_ORDER, 0, 3, "Message not posted");
  2801.         dprint(4, (debugfile, "no post, continuing\n"));
  2802.         continue;
  2803.         }
  2804.  
  2805.         if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
  2806.          || outgoing->newsgroups || (fcc && fcc[0]))){
  2807.         q_status_message(SM_ORDER, 3, 4, "No recipients specified!");
  2808.         dprint(4, (debugfile, "no recip, continuing\n"));
  2809.         continue;
  2810.         }
  2811.  
  2812.         if(check_addresses(&header)){
  2813.         /*--- Addresses didn't check out---*/
  2814.         dprint(4, (debugfile, "addrs failed, continuing\n"));
  2815.         continue;
  2816.         }
  2817.  
  2818.         set_last_fcc(fcc);
  2819.  
  2820.             /*---- Check out fcc -----*/
  2821.             if(fcc && *fcc){
  2822.         local_written = 0;
  2823.             if(!open_fcc(fcc, &fcc_cntxt, 0)
  2824.            || !(local_so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS))){
  2825.             /* ---- Open or allocation of fcc failed ----- */
  2826.                     q_status_message(SM_ORDER, 3, 5,
  2827.                      "Message NOT sent or written to fcc.");
  2828.             dprint(4, (debugfile,"can't open/allocate fcc, cont'g\n"));
  2829.  
  2830.             /*
  2831.              * Find field entry associated with fcc, and start
  2832.              * composer on it...
  2833.              */
  2834.             for(pf = header.local; pf && pf->name; pf = pf->next)
  2835.               if(pf->type == Fcc && pf->he)
  2836.             pf->he->start_here = 1;
  2837.  
  2838.             continue;
  2839.         }
  2840.             }
  2841.         else
  2842.           local_so = NULL;
  2843.  
  2844.             /*---- recompute message-id to encode body info stats ----*/
  2845.             update_message_id(outgoing, mime_stats(*body));
  2846.  
  2847.             /*---- Take care of any requested prefiltering ----*/
  2848.         if(sending_filter_requested
  2849.            && !filter_message_text(sending_filter_requested, outgoing,
  2850.                        *body, &orig_so)){
  2851.         q_status_message1(SM_ORDER, 3, 3,
  2852.                  "Problem filtering!  Nothing sent%s.",
  2853.                  fcc ? " or saved to fcc" : "");
  2854.         continue;
  2855.         }
  2856.  
  2857.             /*------ Actually post  -------*/
  2858.             if(outgoing->newsgroups){
  2859.         /* Turn on references header */
  2860.         if(ref_list){
  2861.             pf_ref->writehdr  = 1;
  2862.             pf_ref->localcopy = 1;
  2863.         }
  2864.  
  2865.         if(news_poster(&header, *body) < 0){
  2866.             dprint(1, (debugfile, "Post failed, continuing\n"));
  2867.             continue;
  2868.         }
  2869.         else
  2870.           result |= P_NEWS_WIN;
  2871.         }
  2872.  
  2873.         /*
  2874.          * BUG: IF we've posted the message *and* an fcc was specified
  2875.          * then we've already got a neatly formatted message in the
  2876.          * local_so.  It'd be nice not to have to re-encode everything
  2877.          * to insert it into the smtp slot...
  2878.          */
  2879.  
  2880.         /*
  2881.          * Turn on "undisclosed recipients" header if no To or cc.
  2882.          */
  2883.             if(!(outgoing->to || outgoing->cc || outgoing->newsgroups)
  2884.           && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
  2885.         pf_nobody->writehdr  = 1;
  2886.         pf_nobody->localcopy = 1;
  2887.         }
  2888.  
  2889. #if    defined(BACKGROUND_POST) && defined(SIGCHLD)
  2890.         /*
  2891.          * If requested, launch backgroud posting...
  2892.          */
  2893.         if(background_requested && !verbose_requested){
  2894.         ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
  2895.         memset(ps_global->post, 0, sizeof(POST_S));
  2896.         if(fcc)
  2897.           ps_global->post->fcc = cpystr(fcc);
  2898.  
  2899.         if((ps_global->post->pid = fork()) == 0){
  2900.             int rv;
  2901.  
  2902.             /*
  2903.              * Put us in new process group...
  2904.              */
  2905.             setpgrp(0, ps_global->post->pid);
  2906.  
  2907.             /* BUG: should fix argv[0] to indicate what we're up to */
  2908.  
  2909.             /*
  2910.              * If there are any live stream, pretend we never
  2911.              * knew them.  Problem is two processes writing
  2912.              * same server process.
  2913.              */
  2914.             ps_global->mail_stream = ps_global->inbox_stream = NULL;
  2915.  
  2916.             /* quell any display output */
  2917.             ps_global->in_init_seq = 1;
  2918.  
  2919.             /*------- Actually mail the message ------*/
  2920.             if(outgoing->to || outgoing->cc
  2921.                || outgoing->bcc || lcc_addr)
  2922.               result |= (call_mailer(&header, *body) > 0)
  2923.                   ? P_MAIL_WIN : P_MAIL_LOSE;
  2924.  
  2925.             /*----- Was there an fcc involved? -----*/
  2926.             if(local_so){
  2927.             /*------ Write it if at least something worked ------*/
  2928.             if((result & (P_MAIL_WIN | P_NEWS_WIN))
  2929.                || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
  2930.                    && pine_rfc822_output(&header, *body,
  2931.                              NULL, NULL))){
  2932.                 char label[50];
  2933.  
  2934.                 strcpy(label, "Fcc");
  2935.                 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC))
  2936.                   sprintf(label + 3, " to %.40s", fcc);
  2937.  
  2938.                 /*-- Now actually copy to fcc folder and close --*/
  2939.                 result |= (write_fcc(fcc, fcc_cntxt, local_so,
  2940.                          label))
  2941.                     ? P_FCC_WIN : P_FCC_LOSE;
  2942.             }
  2943.             else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
  2944.                 q_status_message(SM_ORDER, 3, 5,
  2945.                         "Fcc Failed!.  No message saved.");
  2946.                 dprint(1, (debugfile,
  2947.                        "explicit fcc write failed!\n"));
  2948.                 result |= P_FCC_LOSE;
  2949.             }
  2950.  
  2951.             so_give(&local_so);
  2952.             }
  2953.  
  2954.             /* BUG: do something about "Answered" flag */
  2955.             if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
  2956.             /*
  2957.              * Encode child's result in hi-byte of
  2958.              * editor's result
  2959.              */
  2960.             editor_result = ((result << 8) | COMP_GOTHUP);
  2961.             goto fake_hup;
  2962.             }
  2963.  
  2964.             exit(result);
  2965.         }
  2966.  
  2967.         if(ps_global->post->pid > 0){
  2968.             q_status_message(SM_ORDER, 3, 3,
  2969.                      "Message handed off for posting");
  2970.             break;        /* up to our child now */
  2971.         }
  2972.         else{
  2973.             q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2974.                       "Can't fork for send: %s",
  2975.                       error_description(errno));
  2976.             if(ps_global->post->fcc)
  2977.               fs_give((void **) &ps_global->post->fcc);
  2978.  
  2979.             fs_give((void **) &ps_global->post);
  2980.         }
  2981.  
  2982.         if(local_so)    /* throw away unused store obj */
  2983.           so_give(&local_so);
  2984.  
  2985.         continue;        /* if we got here, there was a prob */
  2986.         }
  2987. #endif /* BACKGROUND_POST */
  2988.  
  2989.             /*------- Actually mail the message ------*/
  2990.             if(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)
  2991.           result |= (call_mailer(&header, *body) > 0)
  2992.               ? P_MAIL_WIN : P_MAIL_LOSE;
  2993.  
  2994.         /*----- Was there an fcc involved? -----*/
  2995.             if(local_so){
  2996.         /*------ Write it if at least something worked ------*/
  2997.         if((result & (P_MAIL_WIN | P_NEWS_WIN))
  2998.            || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
  2999.                && pine_rfc822_output(&header, *body, NULL, NULL))){
  3000.             char label[50];
  3001.  
  3002.             strcpy(label, "Fcc");
  3003.             if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC))
  3004.               sprintf(label + 3, " to %.40s", fcc);
  3005.  
  3006.             /*-- Now actually copy to fcc folder and close --*/
  3007.             result |= (write_fcc(fcc, fcc_cntxt, local_so, label))
  3008.                 ? P_FCC_WIN : P_FCC_LOSE;
  3009.         }
  3010.         else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
  3011.             q_status_message(SM_ORDER,3,5,
  3012.             "Fcc Failed!.  No message saved.");
  3013.             dprint(1, (debugfile, "explicit fcc write failed!\n"));
  3014.             result |= P_FCC_LOSE;
  3015.         }
  3016.  
  3017.         so_give(&local_so);
  3018.         }
  3019.  
  3020.             /*----- Mail Post FAILED, back to composer -----*/
  3021.             if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
  3022.         dprint(1, (debugfile, "Send failed, continuing\n"));
  3023.         continue;
  3024.         }
  3025.  
  3026.         /*
  3027.          * If message sent *completely* successfully, there's a
  3028.          * reply_list AND we're allowed to write back state, do it.
  3029.          * But also protect against shifted message numbers due 
  3030.          * to new mail arrival.  Since the number passed is based
  3031.          * on the real imap msg no, AND we're sure no expunge has 
  3032.          * been done, just fix up the sorted number...
  3033.          */
  3034.         if(reply_list && reply_list[0] > -1 && !READONLY_FOLDER){
  3035.         char *seq, *p;
  3036.         long  i, j;
  3037.  
  3038.         for(i = 0L, p = tmp_20k_buf; reply_list[i] != -1L; i++){
  3039.             if(i)
  3040.               sstrcpy(&p, ",");
  3041.  
  3042.             sstrcpy(&p, long2string(reply_list[i]));
  3043.             if(j = mn_raw2m(ps_global->msgmap, reply_list[i]))
  3044.               clear_index_cache_ent(j);
  3045.         }
  3046.  
  3047.         seq = cpystr(tmp_20k_buf);
  3048.         mail_setflag(ps_global->mail_stream, seq, "\\ANSWERED");
  3049.         fs_give((void **)&seq);
  3050.         check_point_change();
  3051.         }
  3052.  
  3053.             /*----- Signed, sealed, delivered! ------*/
  3054.         q_status_message(SM_ORDER, 0, 3,
  3055.                  pine_send_status(result, fcc, tmp_20k_buf, NULL));
  3056.  
  3057.             break; /* All's well, pop out of here */
  3058.         }
  3059.     }
  3060.  
  3061.     if(orig_so)
  3062.       so_give(&orig_so);
  3063.  
  3064.     if(fcc)
  3065.       fs_give((void **)&fcc);
  3066.  
  3067.     free_attachment_list(&pbuf.attachments);
  3068.     for(i=0; i < fixed_cnt; i++){
  3069.     if(pfields[i].textbuf)
  3070.       fs_give((void **)&pfields[i].textbuf);
  3071.  
  3072.     fs_give((void **)&pfields[i].name);
  3073.     }
  3074.  
  3075.     if(lcc_addr)
  3076.       mail_free_address(&lcc_addr);
  3077.     
  3078.     if(nobody_addr)
  3079.       mail_free_address(&nobody_addr);
  3080.     
  3081.     free_customs(header.custom);
  3082.     fs_give((void **)&pfields);
  3083.     for(he = headents; he->name; he++)
  3084.       if(he->bldr_private)
  3085.     fs_give((void **)&he->bldr_private);
  3086.  
  3087.     fs_give((void **)&headents);
  3088.     fs_give((void **)&sending_order);
  3089.     dprint(4, (debugfile, "=== send returning ===\n"));
  3090. }
  3091.  
  3092.  
  3093.  
  3094. /*----------------------------------------------------------------------
  3095.    Build a status message suitable for framing
  3096.  
  3097. Returns: pointer to resulting buffer
  3098.   ---*/
  3099. char *
  3100. pine_send_status(result, fcc_name, buf, goodorbad)
  3101.     int   result;
  3102.     char *fcc_name;
  3103.     char *buf;
  3104.     int  *goodorbad;
  3105. {
  3106.     sprintf(buf, "Message %s%s%s%s%s%s%s.",
  3107.         (result & P_NEWS_WIN)
  3108.            ? "posted" 
  3109.            : (result & P_NEWS_LOSE)
  3110.                 ? "NOT posted" : "",
  3111.         ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
  3112.          && (result & P_FCC_BITS))
  3113.           ? ", "
  3114.           : ((result & P_NEWS_BITS) && (result & P_MAIL_BITS))
  3115.               ? " and " : "",
  3116.         (result & P_MAIL_WIN)
  3117.           ? "sent"
  3118.           : (result & P_MAIL_LOSE)
  3119.               ? "NOT SENT" : "",
  3120.         ((result & (P_MAIL_BITS | P_NEWS_BITS)) && (result & P_FCC_BITS))
  3121.           ? " and copied to " 
  3122.           : (result & P_FCC_WIN) ? "ONLY copied to " : "",
  3123.         (result & P_FCC_WIN) ? "\"" : "",
  3124.         (result & P_FCC_WIN) ? fcc_name  : "",
  3125.         (result & P_FCC_WIN) ? "\"" : "");
  3126.  
  3127.     if(goodorbad)
  3128.       *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
  3129.  
  3130.     return(buf);
  3131. }
  3132.  
  3133.  
  3134.  
  3135. /*----------------------------------------------------------------------
  3136.    Check for addresses the user is not permitted to send to, or probably
  3137.    doesn't want to send to
  3138.    
  3139. Returns: 0 if OK, and 1 if the message shouldn't be sent
  3140.  
  3141. Queues a message indicating what happened
  3142.   ---*/
  3143. int
  3144. check_addresses(header)
  3145.     METAENV *header;
  3146. {
  3147.     PINEFIELD *pf;
  3148.     ADDRESS *a;
  3149.     int         send_daemon = 0;
  3150.  
  3151.     /*---- Is he/she trying to send mail to the mailer-daemon ----*/
  3152.     for(pf = header->local; pf && pf->name; pf = pf->next)
  3153.       if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
  3154.     for(a = *pf->addr; a != NULL; a = a->next){
  3155.         if(a->host && (a->host[0] == '.'
  3156.                || (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
  3157.                    && a->host[0] == '@'))){
  3158.         q_status_message2(SM_ORDER, 4, 7,
  3159.                   "Can't send to address %s: %s",
  3160.                   a->mailbox,
  3161.                   (a->host[0] == '.')
  3162.                     ? a->host
  3163.                     : "not in addressbook");
  3164.         return(1);
  3165.         }
  3166.         else if(ps_global->restricted
  3167.             && !address_is_us(*pf->addr, ps_global)){
  3168.         q_status_message(SM_ORDER, 3, 3,
  3169.     "Restricted demo version of Pine. You may only send mail to yourself");
  3170.         return(1);
  3171.         }
  3172.         else if(a->mailbox && strucmp(a->mailbox, "mailer-daemon") == 0
  3173.             && !send_daemon){
  3174.         send_daemon = 1;
  3175.         if(want_to("Really send this message to the MAILER-DAEMON",
  3176.                'n', 'n', NO_HELP, 0, 0) == 'n')
  3177.           return(1);
  3178.         }
  3179.     }
  3180.  
  3181.     return(0);
  3182. }
  3183.  
  3184.  
  3185. /*----------------------------------------------------------------------
  3186.     Validate the given subject relative to any news groups.
  3187.      
  3188. Args: none
  3189.  
  3190. Returns: always returns 1, but also returns error if
  3191. ----*/      
  3192. int
  3193. valid_subject(given, expanded, error, fcc)
  3194.     char     *given,
  3195.         **expanded,
  3196.         **error;
  3197.     BUILDER_ARG  *fcc;
  3198. {
  3199.     struct headerentry *hp;
  3200.  
  3201.     if(expanded)
  3202.       *expanded = cpystr(given);
  3203.  
  3204.     if(error){
  3205.     /*
  3206.      * Now look for any header entry we passed to pico that has to do
  3207.      * with news.  If there's no subject, gripe.
  3208.      */
  3209.     for(hp = pbuf.headents; hp->prompt; hp++)
  3210.       if(hp->help == h_composer_news){
  3211.           if(hp->hd_text->text[0] && !*given)
  3212.         *error = cpystr(
  3213.             "News postings MUST have a subject!  Please add one!");
  3214.  
  3215.           break;
  3216.       }
  3217.     }
  3218.  
  3219.     return(0);
  3220. }
  3221.  
  3222.  
  3223.  
  3224. /*----------------------------------------------------------------------
  3225.     Call to map pine's flags into those pico makes available
  3226.  
  3227. Args: ps -- usual pine structure
  3228.  
  3229. Returns: long contining bitmap of pico flags
  3230. ----*/      
  3231. long
  3232. flags_for_pico(ps)
  3233.     struct pine *ps;
  3234. {
  3235.     return((F_ON(F_CAN_SUSPEND, ps)        ? P_SUSPEND : 0L)
  3236.        | (F_ON(F_USE_FK,ps)            ? P_FKEYS : 0L)
  3237.        | (ps->restricted            ? P_SECURE : 0L)
  3238.        | (F_ON(F_ALT_ED_NOW,ps)        ? P_ALTNOW : 0L)
  3239.        | (F_ON(F_USE_CURRENT_DIR,ps)    ? P_CURDIR : 0L)
  3240.        | (F_ON(F_SUSPEND_SPAWNS,ps)        ? P_SUBSHELL : 0L)
  3241.        | (F_ON(F_COMPOSE_MAPS_DEL,ps)    ? P_DELRUBS : 0L)
  3242.        | (F_ON(F_ENABLE_TAB_COMPLETE,ps)    ? P_COMPLETE : 0L)
  3243.        | (F_ON(F_SHOW_CURSOR, ps)        ? P_SHOCUR : 0L)
  3244.        | (F_ON(F_DEL_FROM_DOT, ps)        ? P_DOTKILL : 0L)
  3245.        | (F_ON(F_ENABLE_DOT_FILES, ps)    ? P_DOTFILES : 0L)
  3246.        | (F_ON(F_ALLOW_GOTO, ps)        ? P_ALLOW_GOTO : 0L)
  3247.        | ((F_ON(F_ENABLE_ALT_ED,ps) || F_ON(F_ALT_ED_NOW,ps)
  3248.                 ||(ps->VAR_EDITOR && ps->VAR_EDITOR[0]))
  3249.             ? P_ADVANCED : 0L)
  3250.        | ((!ps->VAR_CHAR_SET || !strucmp(ps->VAR_CHAR_SET, "US-ASCII"))
  3251.             ? P_HIBITIGN: 0L));
  3252. }
  3253.  
  3254.  
  3255.  
  3256. /*----------------------------------------------------------------------
  3257.     Call back for pico to use to check for new mail.
  3258.      
  3259. Args: cursor -- pointer to in to tell caller if cursor location changed
  3260.                 if NULL, turn off cursor positioning.
  3261.       timing -- whether or not it's a good time to check 
  3262.  
  3263.  
  3264. Returns: returns 1 on success, zero on error.
  3265. ----*/      
  3266. long
  3267. new_mail_for_pico(timing, status)
  3268.     int  timing;
  3269.     int  status;
  3270. {
  3271.     int old_cue, rv;
  3272.     int save_defer;
  3273.  
  3274.     /*
  3275.      * If we're not interested in the status, don't display the busy
  3276.      * cue either...
  3277.      */
  3278.     if(!status){
  3279.     old_cue = F_ON(F_SHOW_DELAY_CUE, ps_global);
  3280.     F_SET(F_SHOW_DELAY_CUE, ps_global, 0);
  3281.     }
  3282.  
  3283.     /* don't know where the cursor's been, reset it */
  3284.     clear_cursor_pos();
  3285.     save_defer = ps_global->sort_is_deferred;
  3286.     /* don't sort while composing, just announce new mail */
  3287.     ps_global->sort_is_deferred = 1;
  3288.     rv = new_mail(0, timing, status);
  3289.     ps_global->sort_is_deferred = save_defer;
  3290.  
  3291.     if(!status)
  3292.       F_SET(F_SHOW_DELAY_CUE, ps_global, old_cue);
  3293.  
  3294.     return(rv);
  3295. }
  3296.  
  3297.  
  3298.  
  3299.  
  3300. /*----------------------------------------------------------------------
  3301.     Call back for pico to insert the specified message's text
  3302.      
  3303. Args: n -- message number to format
  3304.       f -- function to use to output the formatted message
  3305.  
  3306.  
  3307. Returns: returns msg number formatted on success, zero on error.
  3308. ----*/      
  3309. long
  3310. message_format_for_pico(n, f)
  3311.     long n;
  3312.     int  (*f) PROTO((int));
  3313. {
  3314.     ENVELOPE *e;
  3315.     BODY     *b;
  3316.     char     *old_quote = NULL;
  3317.     long      rv = n;
  3318.  
  3319.     if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
  3320.        && (e = mail_fetchstructure(ps_global->mail_stream,
  3321.                    mn_m2raw(ps_global->msgmap, n), &b)))){
  3322.     q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
  3323.     flush_status_messages(0);
  3324.     return(0L);
  3325.     }
  3326.  
  3327.     /* temporarily assign a new quote string */
  3328.     old_quote = pbuf.quote_str;
  3329.     pbuf.quote_str = reply_quote_str(e, 1);
  3330.  
  3331.     /* build separator line */
  3332.     reply_delimiter(e, f);
  3333.  
  3334.     /* actually write message text */
  3335.     if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b,
  3336.                FM_NEW_MESS, f)){
  3337.     q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
  3338.     flush_status_messages(0);
  3339.     rv = 0L;
  3340.     }
  3341.  
  3342.     fs_give((void **)&pbuf.quote_str);
  3343.     pbuf.quote_str = old_quote;
  3344.     return(rv);
  3345. }
  3346.  
  3347.  
  3348.  
  3349. /*----------------------------------------------------------------------
  3350.     Call back for pico to prompt the user for exit confirmation
  3351.  
  3352. Args: dflt -- default answer for confirmation prompt
  3353.  
  3354. Returns: either NULL if the user accepts exit, or string containing
  3355.      reason why the user declined.
  3356. ----*/      
  3357. char *
  3358. send_exit_for_pico()
  3359. {
  3360.     int           i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
  3361.     char      *rstr = NULL, *p, *lc;
  3362.     void     (*redraw)() = ps_global->redrawer;
  3363.     ESCKEY_S   opts[9];
  3364.     struct filters {
  3365.     char  *filter;
  3366.     int    index;
  3367.     struct filters *prev, *next;
  3368.     } *filters = NULL, *fp;
  3369.  
  3370.     sending_filter_requested = NULL;
  3371.     if(old_suspend = F_ON(F_CAN_SUSPEND, ps_global))
  3372.       F_SET(F_CAN_SUSPEND, ps_global, 0);
  3373.  
  3374.     /*
  3375.      * Build list of available filters...
  3376.      */
  3377.     for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
  3378.     for(p = ps_global->VAR_SEND_FILTER[i];
  3379.         *p && !isspace((unsigned char)*p); p++)
  3380.       ;
  3381.  
  3382.     c  = *p;
  3383.     *p = '\0';
  3384.     if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
  3385.          && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
  3386.         *p = c;
  3387.         continue;
  3388.     }
  3389.  
  3390.     fp       = (struct filters *)fs_get(sizeof(struct filters));
  3391.     fp->index  = i;
  3392.     if(lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])){
  3393.         fp->filter = cpystr(lc);
  3394.     }
  3395.     else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
  3396.         sprintf(tmp_20k_buf, "...%s", p - 17);
  3397.         fp->filter = cpystr(tmp_20k_buf);
  3398.     }
  3399.     else
  3400.       fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
  3401.  
  3402.     *p = c;
  3403.  
  3404.     if(filters){
  3405.         fp->next       = filters;
  3406.         fp->prev       = filters->prev;
  3407.         fp->prev->next = filters->prev = fp;
  3408.     }
  3409.     else{
  3410.         filters = (struct filters *)fs_get(sizeof(struct filters));
  3411.         filters->index  = -1;
  3412.         filters->filter = NULL;
  3413.         filters->next = filters->prev = fp;
  3414.         fp->next = fp->prev = filters;
  3415.     }
  3416.     }
  3417.  
  3418.     i = 0;
  3419.     opts[i].ch      = 'y';
  3420.     opts[i].rval    = 'y';
  3421.     opts[i].name    = "Y";
  3422.     opts[i++].label = "Yes";
  3423.  
  3424.     opts[i].ch      = 'n';
  3425.     opts[i].rval    = 'n';
  3426.     opts[i].name    = "N";
  3427.     opts[i++].label = "No";
  3428.  
  3429.     if(filters){
  3430.     /* set global_filter_pointer to desired filter or NULL if none */
  3431.     /* prepare two keymenu slots for selecting filter */
  3432.     opts[i].ch      = ctrl('P');
  3433.     opts[i].rval    = 10;
  3434.     opts[i].name    = "^P";
  3435.     opts[i++].label = "Prev Filter";
  3436.  
  3437.     opts[i].ch      = ctrl('N');
  3438.     opts[i].rval    = 11;
  3439.     opts[i].name    = "^N";
  3440.     opts[i++].label = "Next Filter";
  3441.  
  3442.     if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
  3443.       filters = filters->next;
  3444.     }
  3445.  
  3446.     verbose_requested = 0;
  3447.     if(F_ON(F_VERBOSE_POST, ps_global)){
  3448.     /* setup keymenu slot to toggle verbose mode */
  3449.     opts[i].ch    = ctrl('W');
  3450.     opts[i].rval  = 12;
  3451.     opts[i].name  = "^W";
  3452.     verbose_label = i++;
  3453.     }
  3454.  
  3455. #if    defined(BACKGROUND_POST) && defined(SIGCHLD)
  3456.     background_requested = 0;
  3457.     if(F_ON(F_BACKGROUND_POST, ps_global)){
  3458.     opts[i].ch    = ctrl('R');
  3459.     opts[i].rval  = 15;
  3460.     opts[i].name  = "^R";
  3461.     bg_label = i++;
  3462.     }
  3463. #endif
  3464.  
  3465.     if(filters){
  3466.     opts[i].ch      = KEY_UP;
  3467.     opts[i].rval    = 10;
  3468.     opts[i].name    = "";
  3469.     opts[i++].label = "";
  3470.  
  3471.     opts[i].ch      = KEY_DOWN;
  3472.     opts[i].rval    = 11;
  3473.     opts[i].name    = "";
  3474.     opts[i++].label = "";
  3475.     }
  3476.  
  3477.     opts[i].ch = -1;
  3478.  
  3479.     ps_global->redrawer = NULL;
  3480.     fix_windsize(ps_global);
  3481.  
  3482.     while(1){
  3483.     if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
  3484.       *p = '\0';
  3485.     else
  3486.       p = NULL;
  3487.  
  3488.     sprintf(tmp_20k_buf, "Send message%s%s%s%s%s%s%s%s%s%s? ",
  3489.         (filters || verbose_requested || background_requested)
  3490.           ? " (" : "",
  3491.         (filters && filters->filter) ? "filtered thru \"" : "",
  3492.         (filters)
  3493.           ? (filters->filter
  3494.               ? filters->filter
  3495.               : "unfiltered")
  3496.           : "",
  3497.         (filters && filters->filter) ? "\"" : "",
  3498.         (filters && (verbose_requested || background_requested))
  3499.           ? " " : "",
  3500.         (verbose_requested || background_requested)
  3501.           ? "in " : "",
  3502.         (verbose_requested) ? "verbose " : "",
  3503.         (background_requested) ? "background " : "",
  3504.         (verbose_requested || background_requested)
  3505.           ? "mode" : "",
  3506.         (filters || verbose_requested || background_requested)
  3507.           ? ")" : "");
  3508.  
  3509.     if(p)
  3510.       *p = ' ';
  3511.  
  3512.     if(verbose_label)
  3513.       opts[verbose_label].label = verbose_requested ? "Normal" : "Verbose";
  3514.  
  3515.     if(bg_label)
  3516.       opts[bg_label].label = background_requested
  3517.                    ? "Foreground" : "Background";
  3518.  
  3519. /* BUG: fix resize during prompt!! */
  3520. /* BUG: test kmpopped stuff? */
  3521.     rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
  3522.                'y', 'x', NO_HELP, RB_NORM);
  3523.     if(rv == 'y'){                /* user ACCEPTS! */
  3524.         break;
  3525.     }
  3526.     else if(rv == 'n'){            /* Declined! */
  3527.         rstr = "No Message Sent";
  3528.         break;
  3529.     }
  3530.     else if(rv == 'x'){            /* Cancelled! */
  3531.         rstr = "Send Cancelled";
  3532.         break;
  3533.     }
  3534.     else if(rv == 10)            /* PREVIOUS filter */
  3535.       filters = filters->prev;
  3536.     else if(rv == 11)            /* NEXT filter */
  3537.       filters = filters->next;
  3538.     else if(rv == 12){            /* flip verbose bit */
  3539.         if((verbose_requested = !verbose_requested)
  3540.            && background_requested)
  3541.           background_requested = 0;
  3542.     }
  3543.     else if(rv == 15){
  3544.         if((background_requested = !background_requested)
  3545.            && verbose_requested)
  3546.           verbose_requested = 0;
  3547.     }
  3548.     }
  3549.  
  3550.     /* remember selection */
  3551.     if(filters && filters->index > -1)
  3552.       sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
  3553.  
  3554.     if(filters){
  3555.     filters->prev->next = NULL;            /* tie off list */
  3556.     while(filters){                /* then free it */
  3557.         fp = filters->next;
  3558.         if(filters->filter)
  3559.           fs_give((void **)&filters->filter);
  3560.  
  3561.         fs_give((void **)&filters);
  3562.         filters = fp;
  3563.     }
  3564.     }
  3565.  
  3566.     if(old_suspend)
  3567.       F_SET(F_CAN_SUSPEND, ps_global, 1);
  3568.  
  3569.     ps_global->redrawer = redraw;
  3570.     return(rstr);
  3571. }
  3572.  
  3573.  
  3574.  
  3575. /*----------------------------------------------------------------------
  3576.     Call back for pico to get newmail status messages displayed
  3577.  
  3578. Args: x -- char processed
  3579.  
  3580. Returns: 
  3581. ----*/      
  3582. int
  3583. display_message_for_pico(x)
  3584.     int x;
  3585. {
  3586.     clear_cursor_pos();            /* can't know where cursor is */
  3587.     mark_status_dirty();        /* don't count on cached text */
  3588.     return(display_message(x));
  3589. }
  3590.  
  3591.  
  3592.  
  3593. /*----------------------------------------------------------------------
  3594.     Call back for pico to get desired directory for its check point file
  3595.      
  3596.   Args: s -- buffer to write directory name
  3597.     n -- length of that buffer
  3598.  
  3599.   Returns: pointer to static buffer
  3600. ----*/      
  3601. char *
  3602. checkpoint_dir_for_pico(s, n)
  3603.     char *s;
  3604.     int   n;
  3605. {
  3606. #if defined(DOS) || defined(OS2)
  3607.     /*
  3608.      * we can't assume anything about root or home dirs, so
  3609.      * just plunk it down in the same place as the pinerc
  3610.      */
  3611.     if(!getenv("HOME")){
  3612.     char *lc = last_cmpnt(ps_global->pinerc);
  3613.  
  3614.     if(lc != NULL){
  3615.         strncpy(s, ps_global->pinerc, min(n-1,lc-ps_global->pinerc));
  3616.         s[min(n-1,lc-ps_global->pinerc)] = '\0';
  3617.     }
  3618.     else{
  3619.         strncpy(s, ".\\", n-1);
  3620.         s[n-1] = '\0';
  3621.     }
  3622.     }
  3623.     else
  3624. #endif
  3625.     strcpy(s, ps_global->home_dir);
  3626.  
  3627.     return(s);
  3628. }
  3629.  
  3630.  
  3631. /*----------------------------------------------------------------------
  3632.     Call back for pico to display mime type of attachment
  3633.      
  3634. Args: file -- filename being attached
  3635.  
  3636. Returns: returns 1 on success (message queued), zero otherwise (don't know
  3637.       type so nothing queued).
  3638. ----*/      
  3639. int
  3640. mime_type_for_pico(file)
  3641.     char *file;
  3642. {
  3643.     BODY *body;
  3644.     int   rv;
  3645.     void *file_contents;
  3646.  
  3647.     body           = mail_newbody();
  3648.     body->type     = TYPEOTHER;
  3649.     body->encoding = ENCOTHER;
  3650.  
  3651.     /* don't know where the cursor's been, reset it */
  3652.     clear_cursor_pos();
  3653.     if(!set_mime_type_by_extension(body, file)){
  3654.     if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS)) != NULL){
  3655.         body->contents.binary = file_contents;
  3656.         set_mime_type_by_grope(body);
  3657.     }
  3658.     }
  3659.  
  3660.     if(body->type != TYPEOTHER){
  3661.     rv = 1;
  3662.     q_status_message3(SM_ORDER, 0, 3,
  3663.         "File %s attached as type %s/%s", file,
  3664.         body_types[body->type],
  3665.         body->subtype ? body->subtype : rfc822_default_subtype(body->type));
  3666.     }
  3667.     else
  3668.       rv = 0;
  3669.  
  3670.     pine_free_body(&body);
  3671.     return(rv);
  3672. }
  3673.  
  3674.  
  3675.  
  3676. /*----------------------------------------------------------------------
  3677.   Call back for pico to receive an uploaded message
  3678.  
  3679.   Args: fname -- name for uploaded file (empty if they want us to assign it)
  3680.     size -- pointer to long to hold the attachment's size
  3681.  
  3682.   Notes: the attachment is uploaded to a temp file, and 
  3683.  
  3684.   Returns: TRUE on success, FALSE otherwise
  3685. ----*/
  3686. int
  3687. upload_msg_to_pico(fname, size)
  3688.     char *fname;
  3689.     long *size;
  3690. {
  3691.     char     cmd[MAXPATH+1], prefix[1024], *fnp;
  3692.     long     l;
  3693.     PIPE_S  *syspipe;
  3694.  
  3695.     dprint(1, (debugfile, "Upload cmd called to xfer \"%s\"\n",
  3696.            fname ? fname : "<NO FILE>"));
  3697.  
  3698.     if(!fname)                /* no place for file name */
  3699.       return(0);
  3700.  
  3701.     if(!*fname){            /* caller wants temp file */
  3702.     strcpy(fname, fnp = temp_nam(NULL, "pu"));
  3703.     fs_give((void **)&fnp);
  3704.     }
  3705.  
  3706.     build_updown_cmd(cmd, ps_global->VAR_UPLOAD_CMD_PREFIX,
  3707.              ps_global->VAR_UPLOAD_CMD, fname);
  3708.     if(syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET)){
  3709.     (void) close_system_pipe(&syspipe);
  3710.     if((l = name_file_size(fname)) < 0L){
  3711.         q_status_message2(SM_ORDER | SM_DING, 3, 4,
  3712.                   "Error determining size of %s: %s", fname,
  3713.                   fnp = error_description(errno));
  3714.         dprint(1, (debugfile,
  3715.                "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
  3716.                cmd, fname, fnp));
  3717.     }
  3718.     else if(size)
  3719.       *size = l;
  3720.  
  3721.     return(l >= 0);
  3722.     }
  3723.     else
  3724.       q_status_message(SM_ORDER | SM_DING, 3, 4, "Error opening pipe");
  3725.  
  3726.     return(0);
  3727. }
  3728.  
  3729.  
  3730.  
  3731. /*----------------------------------------------------------------------
  3732.   Call back for pico to tell us the window size's changed
  3733.  
  3734.   Args: none
  3735.  
  3736.   Returns: none (but pine's ttyo structure may have been updated)
  3737. ----*/
  3738. void
  3739. resize_for_pico()
  3740. {
  3741.     fix_windsize(ps_global);
  3742. }
  3743.  
  3744.  
  3745.  
  3746.  
  3747. /*----------------------------------------------------------------------
  3748.     Pass the first text segment of the message thru the "send filter"
  3749.      
  3750. Args: body pointer and address for storage object of old data
  3751.  
  3752. Returns: returns 1 on success, zero on error.
  3753. ----*/      
  3754. int
  3755. filter_message_text(fcmd, outgoing, body, old)
  3756.     char      *fcmd;
  3757.     ENVELOPE  *outgoing;
  3758.     BODY      *body;
  3759.     STORE_S  **old;
  3760. {
  3761.     char     *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL;
  3762.     int          key = 0, rv;
  3763.     gf_io_t   gc, pc;
  3764.     STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
  3765.                 ? &body->contents.part->body.contents.binary
  3766.                 : &body->contents.binary),
  3767.          *tmp_so = NULL, *tmpf_so;
  3768.  
  3769.     if(fcmd && (cmd=expand_filter_tokens(fcmd,outgoing,&tmpf,&resultf,&key))){
  3770.     if(tmpf){
  3771.         if(tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS)){
  3772. #ifndef    DOS
  3773.         chmod(tmpf, 0600);
  3774. #endif
  3775.         so_seek(*so, 0L, 0);
  3776.         gf_set_so_readc(&gc, *so);
  3777.         gf_set_so_writec(&pc, tmpf_so);
  3778.         gf_filter_init();
  3779.         if(key){
  3780.             so_puts(tmpf_so, filter_session_key());
  3781.             so_puts(tmpf_so, NEWLINE);
  3782.         }
  3783.  
  3784.         errstr = gf_pipe(gc, pc);
  3785.         so_give(&tmpf_so);
  3786.         }
  3787.         else
  3788.           errstr = "Can't create space for filter temporary file.";
  3789.     }
  3790.  
  3791.     if(!errstr){
  3792.         if(tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)){
  3793.         gf_set_so_writec(&pc, tmp_so);
  3794.         ps_global->mangled_screen = 1;
  3795.         suspend_busy_alarm();
  3796.         ClearScreen();
  3797.         fflush(stdout);
  3798.         if(tmpf){
  3799.             PIPE_S *fpipe;
  3800.  
  3801.             if(fpipe = open_system_pipe(cmd, NULL, NULL,
  3802.                         PIPE_NOSHELL | PIPE_RESET)){
  3803.             if(close_system_pipe(&fpipe) == 0){
  3804.                 if(tmpf_so = so_get(FileStar, tmpf, READ_ACCESS)){
  3805.                 gf_set_so_readc(&gc, tmpf_so);
  3806.                 gf_filter_init();
  3807.                 errstr = gf_pipe(gc, pc);
  3808.                 so_give(&tmpf_so);
  3809.                 }
  3810.                 else
  3811.                   errstr = "Can't open temp file filter wrote.";
  3812.             }
  3813.             else
  3814.               errstr = "Filter command returned error.";
  3815.             }
  3816.             else
  3817.               errstr = "Can't exec filter text.";
  3818.         }
  3819.         else
  3820.           errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
  3821.                      *so, pc, NULL);
  3822.  
  3823.         if(errstr){
  3824.             int ch;
  3825.  
  3826.             fprintf(stdout, "\r\n%s  Hit return to continue.", errstr);
  3827.             fflush(stdout);
  3828.             while((ch = read_char(300)) != ctrl('M')
  3829.               && ch != NO_OP_IDLE)
  3830.               putchar(BELL);
  3831.         }
  3832.         else{
  3833.             BODY *b = (body->type == TYPEMULTIPART)
  3834.                        ? &body->contents.part->body : body;
  3835.  
  3836.             *old = *so;            /* save old so */
  3837.             *so = tmp_so;        /* return new one */
  3838.  
  3839.             /*
  3840.              * Reevaluate the encoding in case the data form's
  3841.              * changed...
  3842.              */
  3843.             b->encoding = ENCOTHER;
  3844.             set_mime_type_by_grope(b);
  3845.         }
  3846.  
  3847.         ClearScreen();
  3848.         resume_busy_alarm();
  3849.         }
  3850.         else
  3851.           errstr = "Can't create space for filtered text.";
  3852.     }
  3853.  
  3854.     fs_give((void **)&cmd);
  3855.     }
  3856.     else
  3857.       return(rv == 0);
  3858.  
  3859.     if(tmpf){
  3860.     unlink(tmpf);
  3861.     fs_give((void **)&tmpf);
  3862.     }
  3863.  
  3864.     if(resultf){
  3865.     if(name_file_size(resultf) > 0L)
  3866.       display_output_file(resultf, "Filter", NULL);
  3867.  
  3868.     fs_give((void **)&resultf);
  3869.     }
  3870.     else if(errstr){
  3871.     if(tmp_so)
  3872.       so_give(&tmp_so);
  3873.  
  3874.     q_status_message1(SM_ORDER | SM_DING, 3, 6, "Problem filtering: %s",
  3875.               errstr);
  3876.     dprint(1, (debugfile, "Filter FAILED: %s\n", errstr));
  3877.     }
  3878.  
  3879.     return(errstr == NULL);
  3880. }
  3881.  
  3882.  
  3883.  
  3884. /*----------------------------------------------------------------------
  3885.      Generate and send a message back to the pine development team
  3886.      
  3887. Args: none
  3888.  
  3889. Returns: none
  3890. ----*/      
  3891. void
  3892. phone_home()
  3893. {
  3894.     int          loser;
  3895.     char      tmp[MAX_ADDRESS], *from_addr;
  3896.     ENVELOPE *outgoing;
  3897.     BODY     *body;
  3898.  
  3899. #if defined(DOS) || defined(OS2)
  3900.     if(!dos_valid_from(0))
  3901.       return;
  3902. #endif
  3903.  
  3904.     outgoing = mail_newenvelope();
  3905.     sprintf(tmp, "pine%s@%s", PHONE_HOME_VERSION, PHONE_HOME_HOST);
  3906.     rfc822_parse_adrlist(&outgoing->to, tmp, ps_global->maildomain);
  3907.     outgoing->message_id  = generate_message_id(ps_global);
  3908.     outgoing->subject      = cpystr("Document Request");
  3909.  
  3910.     body       = mail_newbody();
  3911.     body->type = TYPETEXT;
  3912.  
  3913.     if(body->contents.binary = (void *)so_get(PicoText,NULL,EDIT_ACCESS)){
  3914.     so_puts((STORE_S *)body->contents.binary, "Document request: Pine");
  3915.     so_puts((STORE_S *)body->contents.binary, PHONE_HOME_VERSION);
  3916.     if(ps_global->first_time_user)
  3917.       so_puts((STORE_S *)body->contents.binary, " for New Users");
  3918.  
  3919.     if(ps_global->VAR_INBOX_PATH && ps_global->VAR_INBOX_PATH[0] == '{')
  3920.       so_puts((STORE_S *)body->contents.binary, " and IMAP");
  3921.  
  3922.     if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
  3923.           && ps_global->VAR_NNTP_SERVER[0][0])
  3924.       so_puts((STORE_S *)body->contents.binary, " and NNTP");
  3925.  
  3926.     loser = pine_simple_send(outgoing, &body, NULL, NULL, NULL, 0);
  3927.  
  3928.     q_status_message2(SM_ORDER, 0, 3,"%sequest sent from \"%s\"",
  3929.               (loser) ? "No r" : "R",
  3930.               from_addr = addr_list_string(outgoing->from,NULL,1));
  3931.     fs_give((void **)&from_addr);
  3932.     }
  3933.     else
  3934.       q_status_message(SM_ORDER | SM_DING, 3, 4,
  3935.                "Problem creating space for message text.");
  3936.  
  3937.     mail_free_envelope(&outgoing);
  3938.     pine_free_body(&body);
  3939.  
  3940. }
  3941.  
  3942.  
  3943. /*----------------------------------------------------------------------
  3944.      Call the mailer, SMTP, sendmail or whatever
  3945.      
  3946. Args: header -- full header (envelope and local parts) of message to send
  3947.       body -- The full body of the message including text
  3948.       verbose -- flag to indicate verbose transaction mode requested
  3949.  
  3950. Returns: -1 if failed, 1 if succeeded
  3951. ----*/      
  3952. int
  3953. call_mailer(header, body)
  3954.     METAENV *header;
  3955.     BODY    *body;
  3956. {
  3957.     char         error_buf[100], *error_mess = NULL, *postcmd = NULL;
  3958.     ADDRESS     *a;
  3959.     ENVELOPE    *fake_env = NULL;
  3960.     int          addr_error_count, we_cancel = 0;
  3961.     long     smtp_opts = 0L;
  3962.     char    *verbose_file = NULL;
  3963.     PIPE_S    *postpipe;
  3964.     BODY    *bp = NULL;
  3965.     PINEFIELD    *pf;
  3966.  
  3967. #define MAX_ADDR_ERROR 2  /* Only display 2 address errors */
  3968.  
  3969.     dprint(4, (debugfile, "Sending mail...\n"));
  3970.  
  3971.     /* Check for any recipients */
  3972.     for(pf = header->local; pf && pf->name; pf = pf->next)
  3973.       if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
  3974.     break;
  3975.  
  3976.     if(!pf){
  3977.     q_status_message(SM_ORDER,3,3,
  3978.         "Can't send message. No recipients specified!");
  3979.     return(0);
  3980.     }
  3981.  
  3982.     /* set up counts and such to keep track sent percentage */
  3983.     send_bytes_sent = 0;
  3984.     gf_filter_init();                /* zero piped byte count, 'n */
  3985.     send_bytes_to_send = send_body_size(body);    /* count body bytes         */
  3986.     ps_global->c_client_error[0] = error_buf[0] = '\0';
  3987.     we_cancel = busy_alarm(1, "Sending mail",
  3988.                send_bytes_to_send ? sent_percent : NULL, 1);
  3989.  
  3990.     /* try posting via local "<mta> <-t>" if specified */
  3991.     if(mta_handoff(header, body, error_buf)){
  3992.     if(error_buf[0])
  3993.       error_mess = error_buf;
  3994.  
  3995.     goto done;
  3996.     }
  3997.  
  3998.     /*
  3999.      * If the user's asked for it, and we find that the first text
  4000.      * part (attachments all get b64'd) is non-7bit, ask for ESMTP.
  4001.      */
  4002.     if(F_ON(F_ENABLE_8BIT, ps_global) && (bp = first_text_8bit(body)))
  4003.        smtp_opts |= SOP_ESMTP;
  4004.  
  4005. #ifdef    DEBUG
  4006.     if(debug > 5 || verbose_requested)
  4007.       smtp_opts |= SOP_DEBUG;
  4008. #endif
  4009.  
  4010.  
  4011.     /*
  4012.      * Set global header pointer so post_rfc822_output can get at it when
  4013.      * it's called back from c-client's sending routine...
  4014.      */
  4015.     send_header = header;
  4016.  
  4017.     /*
  4018.      * Fabricate a fake ENVELOPE to hand c-client's SMTP engine.
  4019.      * The purpose is to give smtp_mail the list for SMTP RCPT when
  4020.      * there are recipients in pine's METAENV that are outside c-client's
  4021.      * envelope.
  4022.      *  
  4023.      * NOTE: If there aren't any, don't bother.  Dealt with it below.
  4024.      */
  4025.     for(pf = header->local; pf && pf->name; pf = pf->next)
  4026.       if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr
  4027.      && !(*pf->addr == header->env->to || *pf->addr == header->env->cc
  4028.           || *pf->addr == header->env->bcc))
  4029.     break;
  4030.  
  4031.     if(pf && pf->name){
  4032.     ADDRESS **tail;
  4033.  
  4034.     fake_env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
  4035.     memset(fake_env, 0, sizeof(ENVELOPE));
  4036.     fake_env->return_path = rfc822_cpy_adr(header->env->return_path);
  4037.     tail = &(fake_env->to);
  4038.     for(pf = header->local; pf && pf->name; pf = pf->next)
  4039.       if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
  4040.           *tail = rfc822_cpy_adr(*pf->addr);
  4041.           while(*tail)
  4042.         tail = &((*tail)->next);
  4043.       }
  4044.     }
  4045.  
  4046.     /*
  4047.      * Install our rfc822 output routine 
  4048.      */
  4049.     sending_hooks.rfc822_out = mail_parameters(NULL, GET_RFC822OUTPUT, NULL);
  4050.     (void)mail_parameters(NULL, SET_RFC822OUTPUT, (void *)post_rfc822_output);
  4051.  
  4052.     /*
  4053.      * Allow for verbose posting
  4054.      */
  4055.     (void)mail_parameters(NULL, SET_POSTVERBOSE,(void *)pine_smtp_verbose_out);
  4056.  
  4057.     /*
  4058.      * OK, who posts what?  We tried an mta_handoff above, but there
  4059.      * was either none specified or we decided not to use it.  So,
  4060.      * if there's an smtp-server defined anywhere, 
  4061.      */
  4062.     if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
  4063.        && ps_global->VAR_SMTP_SERVER[0][0]){
  4064.     /*---------- SMTP ----------*/
  4065.     dprint(4, (debugfile, "call_mailer: via TCP\n"));
  4066.     ps_global->noshow_error = 1;
  4067.     TIME_STAMP("smtp-open start (tcp)", 1);
  4068.     sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts);
  4069.     ps_global->noshow_error = 0;
  4070.     }
  4071.     else if(postcmd = smtp_command(ps_global->c_client_error)){
  4072.     /*----- Send via LOCAL SMTP agent ------*/
  4073.     dprint(4, (debugfile, "call_mailer: via pipe\n"));
  4074.     sending_hooks.soutr = mail_parameters(NULL, GET_POSTSOUTR, NULL);
  4075.     sending_hooks.getline = mail_parameters(NULL, GET_POSTGETLINE, NULL);
  4076.     sending_hooks.close = mail_parameters(NULL, GET_POSTCLOSE, NULL);
  4077.     (void)mail_parameters(NULL, SET_POSTSOUTR, (void *)pine_pipe_soutr);
  4078.     (void)mail_parameters(NULL, SET_POSTGETLINE,(void *)pine_pipe_getline);
  4079.     (void)mail_parameters(NULL, SET_POSTCLOSE, (void *)pine_pipe_close);
  4080.  
  4081. /* BUG: should provide separate stderr output! */
  4082.     TIME_STAMP("smtp-open start (pipe)", 1);
  4083.     if(postpipe = open_system_pipe(postcmd, NULL, NULL,
  4084.        PIPE_READ|PIPE_STDERR|PIPE_WRITE|PIPE_PROT|PIPE_NOSHELL|PIPE_DESC)){
  4085.         sending_stream = (SMTPSTREAM *) fs_get (sizeof (SMTPSTREAM));
  4086.         memset(sending_stream, 0, sizeof(SMTPSTREAM));
  4087.         sending_stream->tcpstream = (void *) postpipe;
  4088.         TIME_STAMP("smtp greeting (pipe)", 1);
  4089.         if(!smtp_greeting(sending_stream, "localhost", smtp_opts)){
  4090.         smtp_close(sending_stream);
  4091.         sending_stream = NULL;
  4092.         }
  4093.     }
  4094.     else{
  4095.         dprint(1, (debugfile, "Send via pipe failed!\n"));
  4096.         q_status_message(SM_ORDER | SM_DING, 4, 7, "Send Failed!");
  4097.     }
  4098.     }
  4099.  
  4100.     TIME_STAMP("smtp open", 1);
  4101.     if(sending_stream){
  4102.     dprint(1, (debugfile, "Opened SMTP server \"%s\"\n",
  4103.            postcmd ? postcmd : tcp_host(sending_stream->tcpstream)));
  4104.  
  4105.     if(verbose_requested){
  4106.         TIME_STAMP("verbose start", 1);
  4107.         if(verbose_file = temp_nam(NULL, "sd")){
  4108.         if(verbose_send_output = fopen(verbose_file, "w")){
  4109.             if(!pine_smtp_verbose(sending_stream))
  4110.               sprintf(error_mess = error_buf,
  4111.                   "Mail not sent.  VERBOSE mode error%s%.50s.",
  4112.                   (sending_stream && sending_stream->reply)
  4113.                     ? ": ": "",
  4114.                   (sending_stream && sending_stream->reply)
  4115.                     ? sending_stream->reply : "");
  4116.         }
  4117.         else
  4118.           strcpy(error_mess = error_buf,
  4119.              "Can't open tmp file for VERBOSE mode.");
  4120.         }
  4121.         else
  4122.           strcpy(error_mess = error_buf,
  4123.              "Can't create tmp file name for VERBOSE mode.");
  4124.  
  4125.         TIME_STAMP("verbose end", 1);
  4126.     }
  4127.  
  4128.     /*
  4129.      * Before we actually send data, see if we have to protect
  4130.      * the first text body part from getting encoded.  We protect
  4131.      * it from getting encoded in "pine_rfc822_output_body" by
  4132.      * temporarily inventing a synonym for ENC8BIT...
  4133.      */
  4134.     if(bp && sending_stream->ok_8bitmime){
  4135.         int i;
  4136.  
  4137.         for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++)
  4138.           ;
  4139.  
  4140.         if(i > ENCMAX){        /* no empty encoding slots! */
  4141.         bp = NULL;
  4142.         }
  4143.         else {
  4144.         body_encodings[i] = body_encodings[ENC8BIT];
  4145.         bp->encoding = (unsigned short) i;
  4146.         }
  4147.     }
  4148.  
  4149.     TIME_STAMP("smtp start", 1);
  4150.     if(!error_mess && !smtp_mail(sending_stream, "MAIL",
  4151.                      fake_env ? fake_env : header->env, body)){
  4152.         struct headerentry *last_he = NULL;
  4153.  
  4154.         sprintf(error_buf,
  4155.             "Mail not sent. Sending error%s%.40s",
  4156.             (sending_stream && sending_stream->reply) ? ": ": ".",
  4157.             (sending_stream && sending_stream->reply)
  4158.               ? sending_stream->reply : "");
  4159.         dprint(1, (debugfile, error_buf));
  4160.         addr_error_count = 0;
  4161.         for(pf = header->local; pf && pf->name; pf = pf->next)
  4162.           if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
  4163.         for(a = *pf->addr; a != NULL; a = a->next)
  4164.           if(a->error != NULL){
  4165.               if(addr_error_count++ < MAX_ADDR_ERROR){
  4166.               if(pf->he){
  4167.                   if(last_he)    /* start last reported err */
  4168.                 last_he->start_here = 0;
  4169.  
  4170.                   (last_he = pf->he)->start_here = 1;
  4171.               }
  4172.  
  4173.               if(error_mess)    /* previous error? */
  4174.                 q_status_message(SM_ORDER, 4, 7, error_mess);
  4175.  
  4176.               error_mess = tidy_smtp_mess(a->error,
  4177.                               "Mail not sent: %.60s",
  4178.                               error_buf);
  4179.               }
  4180.  
  4181.               dprint(1, (debugfile, "Send Error: \"%s\"\n",
  4182.                  a->error));
  4183.           }
  4184.  
  4185.         if(!error_mess)
  4186.           error_mess = error_buf;
  4187.     }
  4188.  
  4189.     /* repair modified "body_encodings" array? */
  4190.     if(bp && sending_stream->ok_8bitmime)
  4191.       body_encodings[bp->encoding] = NULL;
  4192.  
  4193.     TIME_STAMP("smtp closing", 1);
  4194.     smtp_close(sending_stream);
  4195.     sending_stream = NULL;
  4196.     TIME_STAMP("smtp done", 1);
  4197.     }
  4198.     else if(!error_mess)
  4199.       sprintf(error_mess = error_buf, "Error sending: %.60s",
  4200.           ps_global->c_client_error);
  4201.  
  4202.     if(verbose_file){
  4203.     if(verbose_send_output){
  4204.         TIME_STAMP("verbose start", 1);
  4205.         fclose(verbose_send_output);
  4206.         verbose_send_output = NULL;
  4207.         q_status_message(SM_ORDER, 0, 3, "Verbose SMTP output received");
  4208.         display_output_file(verbose_file, "Verbose SMTP Interaction",NULL);
  4209.         TIME_STAMP("verbose end", 1);
  4210.     }
  4211.  
  4212.     fs_give((void **)&verbose_file);
  4213.     }
  4214.  
  4215.     /*
  4216.      * Restore original 822 emitter...
  4217.      */
  4218.     (void) mail_parameters(NULL, SET_RFC822OUTPUT, sending_hooks.rfc822_out);
  4219.     if(postcmd)
  4220.       fs_give((void **)&postcmd);
  4221.  
  4222.     if(fake_env)
  4223.       mail_free_envelope(&fake_env);
  4224.  
  4225.   done:
  4226.     if(we_cancel)
  4227.       cancel_busy_alarm(0);
  4228.  
  4229.     TIME_STAMP("call_mailer done", 1);
  4230.     /*-------- Did message make it ? ----------*/
  4231.     if(error_mess){
  4232.         /*---- Error sending mail -----*/
  4233.     if(local_so && !local_written)
  4234.       so_give(&local_so);
  4235.  
  4236.         q_status_message(SM_ORDER | SM_DING, 4, 7, error_mess);
  4237.     dprint(1, (debugfile, "call_mailer ERROR: %s\n", error_mess));
  4238.     return(-1);
  4239.     }
  4240.     else{
  4241.     local_written = 1;
  4242.     return(1);
  4243.     }
  4244. }
  4245.  
  4246.  
  4247. /*----------------------------------------------------------------------
  4248.     Checks to make sure the fcc is available and can be opened
  4249.  
  4250. Args: fcc -- the name of the fcc to create.  It can't be NULL.
  4251.       fcc_cntxt -- Returns the context the fcc is in.
  4252.       force -- supress user option prompt
  4253.  
  4254. Returns 0 on failure, 1 on success
  4255.   ----*/
  4256. open_fcc(fcc, fcc_cntxt, force)
  4257.     char       *fcc;
  4258.     CONTEXT_S **fcc_cntxt;
  4259.     int      force;
  4260. {
  4261.     MAILSTREAM *create_stream;
  4262.     int        ok = 1;
  4263.  
  4264.     *fcc_cntxt = NULL;
  4265.  
  4266.     /* 
  4267.      * check for fcc's existance...
  4268.      */
  4269.     TIME_STAMP("open_fcc start", 1);
  4270.     if(context_isambig(fcc)){
  4271.     int flip_dot = 0;
  4272.  
  4273.     /*
  4274.      * Don't want to preclude a user from Fcc'ing a .name'd folder
  4275.      */
  4276.     if(F_OFF(F_ENABLE_DOT_FOLDERS, ps_global)){
  4277.         flip_dot = 1;
  4278.         F_TURN_ON(F_ENABLE_DOT_FOLDERS, ps_global);
  4279.     }
  4280.  
  4281.     /*
  4282.      * We only want to set the "context" if fcc is an ambiguous
  4283.      * name.  Otherwise, our "relativeness" rules for contexts 
  4284.      * (implemented in context.c) might cause the name to be
  4285.      * interpreted in the wrong context...
  4286.      */
  4287.     if(!(*fcc_cntxt = default_save_context(ps_global->context_list)))
  4288.       *fcc_cntxt = ps_global->context_list;
  4289.  
  4290.         find_folders_in_context(NULL, *fcc_cntxt, fcc);
  4291.         if(folder_index(fcc, (*fcc_cntxt)->folders) < 0){
  4292.         if(ps_global->context_list->next){
  4293.         sprintf(tmp_20k_buf,
  4294.             "Folder \"%.20s\" in <%.30s> doesn't exist. Create",
  4295.             fcc, (*fcc_cntxt)->label[0]);
  4296.         }
  4297.         else
  4298.           sprintf(tmp_20k_buf,"Folder \"%.40s\" doesn't exist.  Create",
  4299.               fcc);
  4300.  
  4301.         if(!force && want_to(tmp_20k_buf, 'y', 'n', NO_HELP, 0, 0) != 'y'){
  4302.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  4303.                  "Fcc of message rejected");
  4304.         ok = 0;
  4305.         }
  4306.         else{
  4307.         /*
  4308.          * See if an already open stream will service the create
  4309.          */
  4310.         create_stream = context_same_stream((*fcc_cntxt)->context,
  4311.                             fcc,
  4312.                             ps_global->mail_stream);
  4313.         if(!create_stream
  4314.            && ps_global->mail_stream != ps_global->inbox_stream)
  4315.           create_stream = context_same_stream((*fcc_cntxt)->context,
  4316.                               fcc,
  4317.                               ps_global->inbox_stream);
  4318.  
  4319.         if(!context_create((*fcc_cntxt)->context, create_stream, fcc))
  4320.           ok = 0;
  4321.         }
  4322.         }
  4323.  
  4324.     if(flip_dot)
  4325.       F_TURN_OFF(F_ENABLE_DOT_FOLDERS, ps_global);
  4326.  
  4327.         free_folders_in_context(*fcc_cntxt);
  4328.         if(!ok){
  4329.         TIME_STAMP("open_fcc done.", 1);
  4330.         return(0);
  4331.     }
  4332.     }
  4333.     else if((ok = folder_exists(NULL, fcc)) <= 0){
  4334.         sprintf(tmp_20k_buf,"Folder \"%.40s\" doesn't exist.  Create",fcc);
  4335.  
  4336.         if(ok < 0
  4337.        || (!force && want_to(tmp_20k_buf, 'y', 'n', NO_HELP,0,0) != 'y')){
  4338.         q_status_message(SM_ORDER | SM_DING, 3, 3,
  4339.                  "Fcc of message rejected");
  4340.         TIME_STAMP("open_fcc done.", 1);
  4341.         return(0);
  4342.     }
  4343.  
  4344.     /*
  4345.      * See if an already open stream will service the create
  4346.      */
  4347.     create_stream = same_stream(fcc, ps_global->mail_stream);
  4348.     if(!create_stream && ps_global->mail_stream != ps_global->inbox_stream)
  4349.       create_stream = same_stream(fcc, ps_global->inbox_stream);
  4350.  
  4351.         if(!mail_create(create_stream, fcc)){
  4352.         TIME_STAMP("open_fcc done.", 1);
  4353.         return(0);
  4354.     }
  4355.     }
  4356.  
  4357.     TIME_STAMP("open_fcc done.", 1);
  4358.     return(1);
  4359. }
  4360.  
  4361.  
  4362. /*----------------------------------------------------------------------
  4363.    mail_append() the fcc accumulated in temp_storage to proper destination
  4364.  
  4365. Args:  fcc -- name of folder
  4366.        fcc_cntxt -- context for folder
  4367.        temp_storage -- String of file where Fcc has been accumulated
  4368.  
  4369. This copies the string of file to the actual folder, which might be IMAP
  4370. or a disk folder.  The temp_storage is freed after it is written.
  4371. An error message is produced if this fails.
  4372.   ----*/
  4373. int
  4374. write_fcc(fcc, fcc_cntxt, tmp_storage, label)
  4375.      char      *fcc;
  4376.      CONTEXT_S *fcc_cntxt;
  4377.      STORE_S   *tmp_storage;
  4378.      char      *label;
  4379. {
  4380.     STRING      msg;
  4381.     MAILSTREAM *fcc_stream;
  4382.     char       *cntxt_string;
  4383.     int         we_cancel = 0;
  4384. #ifdef    DOS
  4385.     struct {            /* hack! stolen from dawz.c */
  4386.     int fd;
  4387.     unsigned long pos;
  4388.     } d;
  4389.     extern STRINGDRIVER dawz_string;
  4390. #endif
  4391.  
  4392.     if(!tmp_storage)
  4393.       return(0);
  4394.  
  4395.     TIME_STAMP("write_fcc start.", 1);
  4396.     dprint(4, (debugfile, "Writing %s\n", (label && *label) ? label : ""));
  4397.     if(label && *label){
  4398.     char msg_buf[80];
  4399.  
  4400.     strncat(strcpy(msg_buf, "Writing "), label, 70);
  4401.     we_cancel = busy_alarm(1, msg_buf, NULL, 1);
  4402.     }
  4403.     else
  4404.       we_cancel = busy_alarm(1, NULL, NULL, 0);
  4405.  
  4406.     so_seek(tmp_storage, 0L, 0);
  4407. #ifdef    DOS
  4408.     d.fd  = fileno((FILE *)so_text(tmp_storage));
  4409.     d.pos = 0L;
  4410.     INIT(&msg, dawz_string, (void *)&d, filelength(d.fd));
  4411. #else
  4412.     INIT(&msg, mail_string, (void *)so_text(tmp_storage), 
  4413.          strlen((char *)so_text(tmp_storage)));
  4414. #endif
  4415.  
  4416.     cntxt_string = fcc_cntxt ? fcc_cntxt->context : "[]";
  4417.     fcc_stream   = context_same_stream(cntxt_string, fcc,
  4418.                        ps_global->mail_stream);
  4419.  
  4420.     if(!fcc_stream && ps_global->mail_stream != ps_global->inbox_stream)
  4421.       fcc_stream = context_same_stream(cntxt_string, fcc,
  4422.                        ps_global->inbox_stream);
  4423.  
  4424.     if(!context_append(cntxt_string, fcc_stream, fcc, &msg)){
  4425.     if(we_cancel)
  4426.       cancel_busy_alarm(-1);
  4427.  
  4428.     q_status_message1(SM_ORDER | SM_DING, 3, 5,
  4429.               "Write to \"%s\" FAILED!!!", fcc);
  4430.     dprint(1, (debugfile, "ERROR appending %s in \"%s\"",
  4431.            fcc, cntxt_string));
  4432.     return(0);
  4433.     }
  4434.  
  4435.     if(we_cancel)
  4436.       cancel_busy_alarm(label ? 0 : -1);
  4437.  
  4438.     dprint(4, (debugfile, "done.\n"));
  4439.     TIME_STAMP("write_fcc done.", 1);
  4440.     return(1);
  4441. }
  4442.  
  4443.   
  4444.  
  4445. /*
  4446.  * first_text_8bit - return TRUE if somewhere in the body 8BIT data's
  4447.  *             contained.
  4448.  */
  4449. BODY *
  4450. first_text_8bit(body)
  4451.     BODY *body;
  4452. {
  4453.     if(body->type == TYPEMULTIPART)    /* advance to first contained part */
  4454.       body = &body->contents.part->body;
  4455.  
  4456.     return((body->type == TYPETEXT && body->encoding != ENC7BIT)
  4457.          ? body : NULL);
  4458. }
  4459.  
  4460.  
  4461.  
  4462. /*----------------------------------------------------------------------
  4463.     Remove the leading digits from SMTP error messages
  4464.  -----*/
  4465. char *
  4466. tidy_smtp_mess(error, printstring, outbuf)
  4467.     char *error, *printstring, *outbuf;
  4468. {
  4469.     while(isdigit((unsigned char)*error) || isspace((unsigned char)*error))
  4470.       error++;
  4471.  
  4472.     sprintf(outbuf, printstring, error);
  4473.     return(outbuf);
  4474. }
  4475.  
  4476.         
  4477.     
  4478. /*----------------------------------------------------------------------
  4479.     Set up fields for passing to pico.  Assumes first text part is
  4480.     intended to be passed along for editing, and is in the form of
  4481.     of a storage object brought into existence sometime before pico_send().
  4482.  -----*/
  4483. void
  4484. outgoing2strings(header, bod, text, pico_a)
  4485.     METAENV   *header;
  4486.     BODY      *bod;
  4487.     void     **text;
  4488.     PATMT    **pico_a;
  4489. {
  4490.     PART      *part;
  4491.     PATMT     *pa;
  4492.     char      *type;
  4493.     PINEFIELD *pf;
  4494.     PARAMETER *parms;
  4495.  
  4496.     /*
  4497.      * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
  4498.      * is guaranteed to be of type PicoText!
  4499.      */
  4500.     if(bod->type == TYPETEXT){
  4501.     *text = so_text(bod->contents.binary);
  4502.     } else if(bod->type == TYPEMULTIPART){
  4503.     /*
  4504.      * We used to jump out the window if the first part wasn't text,
  4505.      * but that may not be the case when bouncing a message with
  4506.      * a leading non-text segment.  So, IT'S UNDERSTOOD that the 
  4507.      * contents of the first part to send is still ALWAYS in a 
  4508.      * PicoText storage object, *AND* if that object doesn't contain
  4509.      * data of type text, then it must contain THE ENCODED NON-TEXT
  4510.      * DATA of the piece being sent.
  4511.      *
  4512.      * It's up to the programmer to make sure that such a message is
  4513.      * sent via pine_simple_send and never get to the composer via
  4514.      * pine_send.
  4515.      *
  4516.      * Make sense?
  4517.      */
  4518.     *text = so_text(bod->contents.part->body.contents.binary);
  4519.  
  4520.     /*
  4521.      * If we already had a list, blast it now, so we can build a new
  4522.      * attachment list that reflects what's really there...
  4523.      */
  4524.     if(pico_a)
  4525.       free_attachment_list(pico_a);
  4526.  
  4527.  
  4528.         /* Simplifyihg assumption #28e. (see cross reference) 
  4529.            All parts in the body passed in here that are not already
  4530.            in the attachments list are added to the end of the attachments
  4531.            list. Attachment items not in the body list will be taken care
  4532.            of in strings2outgoing, but they are unlikey to occur
  4533.          */
  4534.  
  4535.         for(part = bod->contents.part->next; part != NULL; part = part->next) {
  4536.             for(pa = *pico_a; pa != NULL; pa = pa->next) {
  4537.                 if(strcmp(pa->id, part->body.id) == 0)
  4538.                   break;  /* Already in list */
  4539.             }
  4540.             if(pa != NULL) /* Was in the list, don't worry 'bout this part */
  4541.               continue;
  4542.  
  4543.             /* to end of list */
  4544.             for(pa = *pico_a;  pa!= NULL && pa->next != NULL;  pa = pa->next);
  4545.             /* empty list or no? */
  4546.             if(pa == NULL) {
  4547.                 /* empty list */
  4548.                 *pico_a = (PATMT *)fs_get(sizeof(PATMT));
  4549.                 pa = *pico_a;
  4550.             } else {
  4551.                 pa->next = (PATMT *)fs_get(sizeof(PATMT));
  4552.                 pa = pa->next;
  4553.             }
  4554.             pa->description = part->body.description == NULL ? cpystr("") : 
  4555.                                               cpystr(part->body.description);
  4556.             
  4557.             type = type_desc(part->body.type,
  4558.                  part->body.subtype,part->body.parameter,0);
  4559.  
  4560.         /*
  4561.          * If we can find a "name" parm, display that too...
  4562.          */
  4563.         for(parms = part->body.parameter; parms; parms = parms->next)
  4564.           if(!strucmp(parms->attribute, "name") && parms->value)
  4565.         break;
  4566.  
  4567.             pa->filename = fs_get(strlen(type)
  4568.                   + (parms ? strlen(parms->value) : 0) + 5);
  4569.  
  4570.             sprintf(pa->filename, "[%s%s%s]", type,
  4571.             parms ? ": " : "", 
  4572.             parms ? parms->value : "");
  4573.             pa->flags    = A_FLIT;
  4574.             pa->size     = cpystr(byte_string(part->body.size.bytes));
  4575.             if(part->body.id == NULL)
  4576.               part->body.id = generate_message_id(ps_global);
  4577.             pa->id       = cpystr(part->body.id);
  4578.             pa->next     = NULL;
  4579.         }
  4580.     }
  4581.         
  4582.  
  4583.     /*------------------------------------------------------------------
  4584.        Malloc strings to pass to composer editor because it expects
  4585.        such strings so it can realloc them
  4586.       -----------------------------------------------------------------*/
  4587.     /*
  4588.      * turn any address fields into text strings
  4589.      */
  4590.     /*
  4591.      * SIMPLIFYING ASSUMPTION #116: all header strings are understood
  4592.      * NOT to be RFC1522 decoded.  Said differently, they're understood
  4593.      * to be RFC1522 ENCODED as necessary.  The intent is to preserve
  4594.      * original charset tagging as far into the compose/send pipe as
  4595.      * we can.
  4596.      */
  4597.     for(pf = header->local; pf && pf->name; pf = pf->next)
  4598.       if(pf->canedit)
  4599.     switch(pf->type){
  4600.       case Address :
  4601.         if(pf->addr){
  4602.         char *p, *t, *u;
  4603.         long  l;
  4604.  
  4605.         pf->scratch = addr_list_string(*pf->addr, NULL, 0);
  4606.  
  4607.         /*
  4608.          * Scan for and fix-up patently bogus fields.
  4609.          *
  4610.          * NOTE: collaboration with this code and what's done in
  4611.          * reply.c:reply_cp_addr to package up the bogus stuff
  4612.          * is required.
  4613.          */
  4614.         for(p = pf->scratch; p = strstr(p, "@.RAW-FIELD."); )
  4615.           for(t = p; ; t--)
  4616.             if(*t == '&'){        /* find "leading" token */
  4617.             *t++ = ' ';        /* replace token */
  4618.             *p = '\0';        /* tie off string */
  4619.             u = rfc822_base64((unsigned char *) t,
  4620.                       (unsigned long) strlen(t),
  4621.                       (unsigned long *) &l);
  4622.             *p = '@';        /* restore 'p' */
  4623.             rplstr(p, 12, "");    /* clear special token */
  4624.             rplstr(t, strlen(t), u);
  4625.             fs_give((void **) &u);
  4626.             if(pf->he)
  4627.               pf->he->start_here = 1;
  4628.  
  4629.             break;
  4630.             }
  4631.             else if(t == pf->scratch)
  4632.               break;
  4633.         }
  4634.  
  4635.         break;
  4636.  
  4637.       case Subject :
  4638.         if(pf->text){
  4639.         if(pf->scratch){
  4640.             char *p, *charset = NULL;
  4641.  
  4642.             p = (char *)fs_get((strlen(pf->scratch)+1)*sizeof(char));
  4643.             if(rfc1522_decode((unsigned char *)p, pf->scratch,&charset)
  4644.                                == (unsigned char *) p){
  4645.             fs_give((void **)&pf->scratch);
  4646.             pf->scratch = p;
  4647.             }
  4648.             else
  4649.               fs_give((void **)&p);
  4650.  
  4651.             if(charset)
  4652.               fs_give((void **)&charset);
  4653.         }
  4654.         else
  4655.           pf->scratch = cpystr((*pf->text) ? *pf->text : "");
  4656.         }
  4657.  
  4658.         break;
  4659.     }
  4660. }
  4661.  
  4662.  
  4663. /*----------------------------------------------------------------------
  4664.     Restore fields returned from pico to form useful to sending
  4665.     routines.
  4666.  -----*/
  4667. void
  4668. strings2outgoing(header, bod, attach)
  4669.     METAENV  *header;
  4670.     BODY    **bod;
  4671.     PATMT    *attach;
  4672. {
  4673.     PINEFIELD *pf;
  4674.     int we_cancel = 0;
  4675.  
  4676.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  4677.  
  4678.     /*
  4679.      * turn any local address strings into address lists
  4680.      */
  4681.     for(pf = header->local; pf && pf->name; pf = pf->next)
  4682.       if(pf->scratch){
  4683.       if(pf->canedit && (!pf->he || pf->he->dirty)){
  4684.           switch(pf->type){
  4685.         case Address :
  4686.           removing_trailing_white_space(pf->scratch);
  4687.           if(*pf->scratch){
  4688.               ADDRESS     *new_addr = NULL;
  4689.               static char *fakedomain = "@";
  4690.  
  4691.               rfc822_parse_adrlist(&new_addr, pf->scratch,
  4692.                    (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
  4693.                      ? fakedomain : ps_global->maildomain);
  4694.               resolve_encoded_entries(new_addr, *pf->addr);
  4695.               mail_free_address(pf->addr);    /* free old addrs */
  4696.               *pf->addr = new_addr;        /* assign new addr */
  4697.           }
  4698.           else
  4699.             mail_free_address(pf->addr);    /* free old addrs */
  4700.  
  4701.           break;
  4702.  
  4703.         case Subject :
  4704.           if(*pf->text)
  4705.             fs_give((void **)pf->text);
  4706.  
  4707.           if(*pf->scratch)
  4708.             *pf->text = cpystr(pf->scratch);
  4709.  
  4710.           break;
  4711.           }
  4712.       }
  4713.  
  4714.       fs_give((void **)&pf->scratch);    /* free now useless text */
  4715.       }
  4716.  
  4717.     create_message_body(bod, attach);
  4718.     pine_encode_body(*bod);
  4719.  
  4720.     if(we_cancel)
  4721.       cancel_busy_alarm(-1);
  4722. }
  4723.  
  4724.  
  4725. /*----------------------------------------------------------------------
  4726.  -----*/
  4727. void
  4728. resolve_encoded_entries(new, old)
  4729.     ADDRESS *new, *old;
  4730. {
  4731.     ADDRESS *a;
  4732.  
  4733.     /* BUG: deal properly with group syntax? */
  4734.     for(; old; old = old->next)
  4735.       if(old->personal && old->mailbox && old->host)
  4736.     for(a = new; a; a = a->next)
  4737.       if(a->personal && a->mailbox && !strcmp(old->mailbox, a->mailbox)
  4738.          && a->host && !strcmp(old->host, a->host)){
  4739.           char *charset = NULL, *p;
  4740.  
  4741.           /*
  4742.            * if we actually found 1522 in the personal name, then
  4743.            * make sure the decoded personal name matches the old 
  4744.            * un-encoded name.  If not, replace the new string with
  4745.            * with the old one.  If 1522 isn't involved, just trust
  4746.            * the change.
  4747.            */
  4748.           p = (char *) rfc1522_decode((unsigned char *)tmp_20k_buf,
  4749.                       old->personal, &charset);
  4750.           if(p == tmp_20k_buf        /* personal was decoded */
  4751.          && !strcmp(a->personal, p)){
  4752.           fs_give((void **)&a->personal);
  4753.           a->personal = cpystr(old->personal);
  4754.           }
  4755.  
  4756.           if(charset)
  4757.         fs_give((void **)&charset);
  4758.  
  4759.           break;
  4760.       }
  4761. }
  4762.  
  4763.  
  4764. /*----------------------------------------------------------------------
  4765.  
  4766.  The head of the body list here is always either TEXT or MULTIPART. It may be
  4767. changed from TEXT to MULTIPART if there are attachments to be added
  4768. and it is not already multipart. 
  4769.   ----*/
  4770. void
  4771. create_message_body(b, attach)
  4772.     BODY  **b;
  4773.     PATMT  *attach;
  4774. {
  4775.     PART         *p, *p_trail;
  4776.     PATMT        *pa;
  4777.     BODY         *b1;
  4778.     void         *file_contents;
  4779.     PARAMETER    *pm;
  4780.     char         *lc;
  4781.  
  4782.     TIME_STAMP("create_body start.", 1);
  4783.     if((*b)->type != TYPEMULTIPART && !attach){
  4784.     /* only override assigned encoding if it might need upgrading */
  4785.     if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
  4786.       (*b)->encoding = ENCOTHER;
  4787.  
  4788.     set_mime_type_by_grope(*b);
  4789.     set_body_size(*b);
  4790.     TIME_STAMP("create_body end.", 1);
  4791.         return;
  4792.     }
  4793.  
  4794.     if((*b)->type == TYPETEXT) {
  4795.         /*-- Current type is text, but there are attachments to add --*/
  4796.         /*-- Upgrade to a TYPEMULTIPART --*/
  4797.         b1                                  = (BODY *)mail_newbody();
  4798.         b1->type                            = TYPEMULTIPART;
  4799.         b1->contents.part                   = mail_newbody_part();
  4800.         b1->contents.part->body             = **b;
  4801.  
  4802.         (*b)->subtype = (*b)->id = (*b)->description = NULL;
  4803.     (*b)->parameter = NULL;
  4804.     (*b)->contents.binary               = NULL;
  4805.     pine_free_body(b);
  4806.         *b = b1;
  4807.     }
  4808.  
  4809.     /*-- Now type must be MULTIPART with first part text --*/
  4810.     (*b)->contents.part->body.encoding = ENCOTHER;
  4811.     set_mime_type_by_grope(&((*b)->contents.part->body));
  4812.     set_body_size(&((*b)->contents.part->body));
  4813.  
  4814.     /*------ Go through the parts list remove those to be deleted -----*/
  4815.     for(p = p_trail = (*b)->contents.part->next; p != NULL;) {
  4816.     for(pa = attach; pa && p->body.id; pa = pa->next)
  4817.       if(pa->id && strcmp(pa->id, p->body.id) == 0){ /* already existed */
  4818.           if(!p->body.description        /* update description? */
  4819.          || strcmp(pa->description, p->body.description)){
  4820.           if(p->body.description)
  4821.             fs_give((void **)&p->body.description);
  4822.  
  4823.           p->body.description = bitstrip(cpystr(pa->description));
  4824.           }
  4825.  
  4826.           break;
  4827.       }
  4828.  
  4829.         if(pa == NULL) {
  4830.             /* attachment wasn't in the list; zap it */
  4831.             if(p == (*b)->contents.part->next) {
  4832.                 /* Beginning of list */
  4833.                 (*b)->contents.part->next = p->next;
  4834.                 p->next = NULL;  /* Don't free the whole chain */
  4835.                 pine_free_body_part(&p);
  4836.                 p = p_trail = (*b)->contents.part->next;
  4837.             } else {
  4838.                 p_trail->next = p->next;
  4839.                 p->next = NULL;  /* Don't free the whole chain */
  4840.                 pine_free_body_part(&p);
  4841.                 p = p_trail->next;
  4842.             }
  4843.         } else {
  4844.             p_trail = p;
  4845.             p       = p->next;
  4846.         }
  4847.     }
  4848.  
  4849.     /*---------- Now add any new attachments ---------*/
  4850.     for(p = (*b)->contents.part ; p->next != NULL; p = p->next);
  4851.     for(pa = attach; pa != NULL; pa = pa->next) {
  4852.         if(pa->id != NULL)
  4853.       continue;            /* Has an ID, it's old */
  4854.  
  4855.     /*
  4856.      * the idea is handle ALL attachments as open FILE *'s.  Actual
  4857.          * encoding and such is handled at the time the message
  4858.          * is shoved into the mail slot or written to disk...
  4859.      *
  4860.          * Also, we never unlink a file, so it's up to whoever opens
  4861.          * it to deal with tmpfile issues.
  4862.      */
  4863.     if((file_contents = (void *)so_get(FileStar, pa->filename,
  4864.                        READ_ACCESS)) == NULL){
  4865.             q_status_message2(SM_ORDER | SM_DING, 3, 4,
  4866.                               "Error \"%s\", couldn't attach file \"%s\"",
  4867.                               error_description(errno), pa->filename);
  4868.             display_message('x');
  4869.             continue;
  4870.         }
  4871.         
  4872.         p->next                      = mail_newbody_part();
  4873.         p                            = p->next;
  4874.         p->body.id                   = generate_message_id(ps_global);
  4875.         p->body.contents.binary      = file_contents;
  4876.  
  4877.     /*
  4878.      * Set type to unknown and let set_mime_type_by_* figure it out.
  4879.      * Always encode attachments we add as BINARY.
  4880.      */
  4881.     p->body.type             = TYPEOTHER;
  4882.     p->body.encoding         = ENCBINARY;
  4883.     p->body.size.bytes           = name_file_size(pa->filename);
  4884.     if(!set_mime_type_by_extension(&p->body, pa->filename))
  4885.       set_mime_type_by_grope(&p->body);
  4886.  
  4887.     so_release((STORE_S *)p->body.contents.binary);
  4888.         p->body.description          = bitstrip(cpystr(pa->description));
  4889.     /* add name attribute */
  4890.     if (p->body.parameter == NULL) {
  4891.         pm = p->body.parameter = mail_newbody_parameter();
  4892.         pm->attribute = cpystr("name");
  4893.     }else {
  4894.             for (pm = p->body.parameter;
  4895.             strucmp(pm->attribute, "name") && pm->next != NULL;
  4896.                                 pm = pm->next);
  4897.         if (strucmp(pm->attribute, "name") != 0) {
  4898.         pm->next = mail_newbody_parameter();
  4899.         pm = pm->next;
  4900.         pm->attribute = cpystr("name");
  4901.         }
  4902.     }
  4903.  
  4904.     pm->value = bitstrip(cpystr((lc = last_cmpnt(pa->filename))
  4905.                       ? lc : pa->filename));
  4906.  
  4907.         p->next = NULL;
  4908.         pa->id = cpystr(p->body.id);
  4909.     }
  4910.  
  4911.     TIME_STAMP("create_body end.", 1);
  4912. }
  4913.  
  4914.  
  4915. /*
  4916.  * Build and return the "From:" address for outbound messages from
  4917.  * global data...
  4918.  */
  4919. ADDRESS *
  4920. generate_from()
  4921. {
  4922.     ADDRESS *addr = mail_newaddr();
  4923.     if(ps_global->VAR_PERSONAL_NAME)
  4924.       addr->personal = cpystr(ps_global->VAR_PERSONAL_NAME);
  4925.  
  4926.     addr->mailbox = cpystr(ps_global->VAR_USER_ID);
  4927.     addr->host    = cpystr(ps_global->maildomain);
  4928.     return(addr);
  4929. }
  4930.  
  4931.  
  4932. /*
  4933.  * free_attachment_list - free attachments in given list
  4934.  */
  4935. void
  4936. free_attachment_list(alist)
  4937.     PATMT  **alist;
  4938. {
  4939.     PATMT  *leading;
  4940.  
  4941.     while(alist && *alist){        /* pointer pointing to something */
  4942.     leading = (*alist)->next;
  4943.     if((*alist)->description)
  4944.           fs_give((void **)&(*alist)->description);
  4945.  
  4946.     if((*alist)->filename){
  4947.         if((*alist)->flags & A_TMP)
  4948.           unlink((*alist)->filename);
  4949.  
  4950.         fs_give((void **)&(*alist)->filename);
  4951.     }
  4952.  
  4953.     if((*alist)->size)
  4954.           fs_give((void **)&(*alist)->size);
  4955.  
  4956.     if((*alist)->id)
  4957.           fs_give((void **)&(*alist)->id);
  4958.  
  4959.     fs_give((void **)alist);
  4960.  
  4961.     *alist = leading;
  4962.     }
  4963. }
  4964.  
  4965.  
  4966.  
  4967. /*----------------------------------------------------------------------
  4968.   Insert the addition into the message id before first "@"
  4969.  
  4970.  This may be called twice on the same message-ID, but it won't do anything
  4971.  the second time. This will cause the status in the message-ID to be wrong
  4972.  if an attempt to send the message is made, an error occurs, and then the
  4973.  size or MIME parts changed and it is sent again.
  4974.   ----*/
  4975. void 
  4976. update_message_id(e, addition)
  4977.     ENVELOPE *e;
  4978.     char *addition;
  4979. {
  4980.     char *p, *q, *r, *new;
  4981.  
  4982.     new = fs_get(strlen(e->message_id) + strlen(addition) + 5);
  4983.     for(p = new, q = e->message_id; *q && *q != '@'; *p++ = *q++);
  4984.     if(p > new && isdigit((unsigned char)*(p-1))) {
  4985.     /* Already been updated if it's a digit, not a letter */
  4986.     fs_give((void **)&new);
  4987.     return;
  4988.     }
  4989.  
  4990.     *p++ = '-';
  4991.     for(r = addition; *r ; *p++ = *r++);
  4992.     for(; *q; *p++ = *q++);
  4993.     *p = *q;
  4994.     fs_give((void **)&(e->message_id));
  4995.     e->message_id = new;
  4996. }
  4997.  
  4998.  
  4999.  
  5000.  
  5001. static struct mime_count {
  5002.     int text_parts;
  5003.     int image_parts;
  5004.     int message_parts;
  5005.     int application_parts;
  5006.     int audio_parts;
  5007.     int  video_parts;
  5008. } mc;
  5009.  
  5010. char *
  5011. mime_stats(body)
  5012.     BODY *body;
  5013. {
  5014.     static char id[10];
  5015.     mc.text_parts = 0;
  5016.     mc.image_parts = 0;
  5017.     mc.message_parts = 0;
  5018.     mc.application_parts = 0;
  5019.     mc.audio_parts = 0;
  5020.     mc.video_parts = 0;
  5021.  
  5022.     mime_recur(body);
  5023.  
  5024.     mc.text_parts        = min(8, mc.text_parts );
  5025.     mc.image_parts       = min(8, mc.image_parts );
  5026.     mc.message_parts     = min(8, mc.message_parts );
  5027.     mc.application_parts = min(8, mc.application_parts );
  5028.     mc.audio_parts       = min(8, mc.audio_parts );
  5029.     mc.video_parts       = min(8, mc.video_parts );
  5030.  
  5031.  
  5032.     id[0] = encode_bits(mc.text_parts);
  5033.     id[1] = encode_bits(mc.message_parts);
  5034.     id[2] = encode_bits(mc.application_parts);
  5035.     id[3] = encode_bits(mc.video_parts);
  5036.     id[4] = encode_bits(mc.audio_parts);
  5037.     id[5] = encode_bits(mc.image_parts);
  5038.     id[6] = '\0';
  5039.     return(id);
  5040. }
  5041.     
  5042.  
  5043.  
  5044. /*----------------------------------------------------------------------
  5045.    ----*/
  5046. void
  5047. mime_recur(body)
  5048.     BODY *body;
  5049. {
  5050.     PART *part;
  5051.     switch (body->type) {
  5052.       case TYPETEXT:
  5053.         mc.text_parts++;
  5054.         break;
  5055.       case TYPEIMAGE:
  5056.         mc.image_parts++;
  5057.         break;
  5058.       case TYPEMESSAGE:
  5059.         mc.message_parts++;
  5060.         break;
  5061.       case TYPEAUDIO:
  5062.         mc.audio_parts++;
  5063.         break;
  5064.       case TYPEAPPLICATION:
  5065.         mc.application_parts++;
  5066.         break;
  5067.       case TYPEVIDEO:
  5068.         mc.video_parts++;
  5069.         break;
  5070.       case TYPEMULTIPART:
  5071.         for(part = body->contents.part; part != NULL; part = part->next) 
  5072.           mime_recur(&(part->body));
  5073.         break;
  5074.     }
  5075. }
  5076.         
  5077. int        
  5078. encode_bits(bits)
  5079.     int bits;
  5080. {
  5081.     if(bits < 10)
  5082.       return(bits + '0');
  5083.     else if(bits < 36)
  5084.       return(bits - 10 + 'a');
  5085.     else if (bits < 62)
  5086.       return(bits - 36 + 'A');
  5087.     else
  5088.       return('.');
  5089. }
  5090.  
  5091.  
  5092. /*
  5093.  * set_mime_type_by_grope - sniff the given storage object to determine its 
  5094.  *                  type, subtype and encoding
  5095.  *
  5096.  *        "Type" and "encoding" must be set before calling this routine.
  5097.  *        If "type" is set to something other than TYPEOTHER on entry,
  5098.  *        then that is the "type" we wish to use.  Same for "encoding"
  5099.  *        using ENCOTHER instead of TYPEOTHER.  Otherwise, we
  5100.  *        figure them out here.  If "type" is already set, we also
  5101.  *        leave subtype alone.  If not, we figure out subtype here.
  5102.  *        There is a chance that we will upgrade "encoding" to a "higher"
  5103.  *        level.  For example, if it comes in as 7BIT we may change
  5104.  *        that to 8BIT if we find a From_ we want to escape.
  5105.  *
  5106.  * NOTE: this is rather inefficient if the store object is a CharStar
  5107.  *       but the win is all types are handled the same
  5108.  */
  5109. void
  5110. set_mime_type_by_grope(body)
  5111.     BODY *body;
  5112. {
  5113. #define RBUFSZ    (8193)
  5114.     unsigned char   *buf, *p, *bol;
  5115.     register size_t  n;
  5116.     long             max_line = 0L,
  5117.                      eight_bit_chars = 0L,
  5118.                      line_so_far = 0L,
  5119.                      len = 0L,
  5120.                      can_be_ascii = 1L;
  5121.     STORE_S         *so = (STORE_S *)body->contents.binary;
  5122.     unsigned short   new_encoding = ENCOTHER;
  5123.     int              we_cancel = 0;
  5124. #ifdef ENCODE_FROMS
  5125.     short            froms = 0, dots = 0,
  5126.                      bmap  = 0x1, dmap = 0x1;
  5127. #endif
  5128.  
  5129.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  5130.  
  5131. #ifndef DOS
  5132.     buf = (unsigned char *)fs_get(RBUFSZ);
  5133. #else
  5134.     buf = (unsigned char *)tmp_20k_buf;
  5135. #endif
  5136.     so_seek(so, 0L, 0);
  5137.  
  5138.     for(n = 0; n < RBUFSZ-1 && so_readc(&buf[n], so) != 0; n++)
  5139.       ;
  5140.  
  5141.     buf[n] = '\0';
  5142.  
  5143.     if(n){    /* check first few bytes to look for magic numbers */
  5144.     if(body->type == TYPEOTHER){
  5145.         if(buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F'){
  5146.         body->type    = TYPEIMAGE;
  5147.         body->subtype = cpystr("GIF");
  5148.         }
  5149.         else if((n > 9) && buf[0] == 0xFF && buf[1] == 0xD8
  5150.             && buf[2] == 0xFF && buf[3] == 0xE0
  5151.             && !strncmp((char *)&buf[6], "JFIF", 4)){
  5152.             body->type    = TYPEIMAGE;
  5153.             body->subtype = cpystr("JPEG");
  5154.         }
  5155.         else if((buf[0] == 'M' && buf[1] == 'M')
  5156.             || (buf[0] == 'I' && buf[1] == 'I')){
  5157.         body->type    = TYPEIMAGE;
  5158.         body->subtype = cpystr("TIFF");
  5159.         }
  5160.         else if((buf[0] == '%' && buf[1] == '!')
  5161.              || (buf[0] == '\004' && buf[1] == '%' && buf[2] == '!')){
  5162.         body->type    = TYPEAPPLICATION;
  5163.         body->subtype = cpystr("PostScript");
  5164.         }
  5165.         else if(buf[0] == '%' && !strncmp((char *)buf+1, "PDF-", 4)){
  5166.         body->type = TYPEAPPLICATION;
  5167.         body->subtype = cpystr("PDF");
  5168.         }
  5169.         else if(buf[0] == '.' && !strncmp((char *)buf+1, "snd", 3)){
  5170.         body->type    = TYPEAUDIO;
  5171.         body->subtype = cpystr("Basic");
  5172.         }
  5173.         else if((n > 3) && buf[0] == 0x00 && buf[1] == 0x05
  5174.                     && buf[2] == 0x16 && buf[3] == 0x00){
  5175.             body->type    = TYPEAPPLICATION;
  5176.         body->subtype = cpystr("APPLEFILE");
  5177.         }
  5178.         else if((n > 3) && buf[0] == 0x50 && buf[1] == 0x4b
  5179.                     && buf[2] == 0x03 && buf[3] == 0x04){
  5180.             body->type    = TYPEAPPLICATION;
  5181.         body->subtype = cpystr("ZIP");
  5182.         }
  5183.  
  5184.         /*
  5185.          * if type was set above, but no encoding specified, go
  5186.          * ahead and make it BASE64...
  5187.          */
  5188.         if(body->type != TYPEOTHER && body->encoding == ENCOTHER)
  5189.           body->encoding = ENCBINARY;
  5190.     }
  5191.     }
  5192.     else{
  5193.     /* PROBLEM !!! */
  5194.     if(body->type == TYPEOTHER){
  5195.         body->type = TYPEAPPLICATION;
  5196.         body->subtype = cpystr("octet-stream");
  5197.         if(body->encoding == ENCOTHER)
  5198.         body->encoding = ENCBINARY;
  5199.     }
  5200.     }
  5201.  
  5202.     if (body->encoding == ENCOTHER || body->type == TYPEOTHER){
  5203. #if defined(DOS) || defined(OS2) /* for binary file detection */
  5204.     int lastchar = '\0';
  5205. #define BREAKOUT 300   /* a value that a character can't be */
  5206. #endif
  5207.  
  5208.     p   = bol = buf;
  5209.     len = n;
  5210.     while (n--){
  5211. /* Some people don't like quoted-printable caused by leading Froms */
  5212. #ifdef ENCODE_FROMS
  5213.         Find_Froms(froms, dots, bmap, dmap, *p);
  5214. #endif
  5215.         if(*p == '\n'){
  5216.         max_line    = max(max_line, line_so_far + p - bol);
  5217.         bol        = NULL;        /* clear beginning of line */
  5218.         line_so_far = 0L;        /* clear line count */
  5219. #if    defined(DOS) || defined(OS2)
  5220.         /* LF with no CR!! */
  5221.         if(lastchar != '\r')        /* must be non-text data! */
  5222.           lastchar = BREAKOUT;
  5223. #endif
  5224.         }
  5225.         else if(*p == ctrl('O') || *p == ctrl('N') || *p == ESCAPE){
  5226.         can_be_ascii--;
  5227.         }
  5228.         else if(*p & 0x80){
  5229.         eight_bit_chars++;
  5230.         }
  5231.         else if(!*p){
  5232.         /* NULL found. Unless we're told otherwise, must be binary */
  5233.         if(body->type == TYPEOTHER){
  5234.             body->type    = TYPEAPPLICATION;
  5235.             body->subtype = cpystr("octet-stream");
  5236.         }
  5237.  
  5238.         /*
  5239.          * The "TYPETEXT" here handles the case that the NULL
  5240.          * comes from imported text generated by some external
  5241.          * editor that permits or inserts NULLS.  Otherwise,
  5242.          * assume it's a binary segment...
  5243.          */
  5244.         new_encoding = (body->type==TYPETEXT) ? ENC8BIT : ENCBINARY;
  5245.  
  5246.         /*
  5247.          * Since we've already set encoding, count this as a 
  5248.          * hi bit char and continue.  The reason is that if this
  5249.          * is text, there may be a high percentage of encoded 
  5250.          * characters, so base64 may get set below...
  5251.          */
  5252.         if(body->type == TYPETEXT)
  5253.           eight_bit_chars++;
  5254.         else
  5255.           break;
  5256.         }
  5257.  
  5258. #if defined(DOS) || defined(OS2) /* for binary file detection */
  5259.         if(lastchar != BREAKOUT)
  5260.           lastchar = *p;
  5261. #endif
  5262.  
  5263.         /* read another buffer in */
  5264.         if(n == 0){
  5265.         if(bol)
  5266.           line_so_far += p - bol;
  5267.  
  5268.         for (n = 0; n < RBUFSZ-1 && so_readc(&buf[n], so) != 0; n++)
  5269.           ;
  5270.  
  5271.         len += n;
  5272.         p = buf;
  5273.         }
  5274.         else
  5275.           p++;
  5276.  
  5277.         /*
  5278.          * If there's no beginning-of-line pointer, then we must
  5279.          * have seen and end-of-line.  Set bol to the start of the
  5280.          * new line...
  5281.          */
  5282.         if(!bol)
  5283.           bol = p;
  5284.  
  5285. #if defined(DOS) || defined(OS2) /* for binary file detection */
  5286.         /* either a lone \r or lone \n indicate binary file */
  5287.         if(lastchar == '\r' || lastchar == BREAKOUT){
  5288.         if(lastchar == BREAKOUT || n == 0 || *p != '\n'){
  5289.             if(body->type == TYPEOTHER){
  5290.             body->type    = TYPEAPPLICATION;
  5291.             body->subtype = cpystr("octet-stream");
  5292.             }
  5293.  
  5294.             new_encoding = ENCBINARY;
  5295.             break;
  5296.         }
  5297.         }
  5298. #endif
  5299.     }
  5300.     }
  5301.  
  5302.     if(body->encoding == ENCOTHER || body->type == TYPEOTHER){
  5303.     /*
  5304.      * Since the type or encoding aren't set yet, fall thru a 
  5305.      * series of tests to make sure an adequate type and 
  5306.      * encoding are set...
  5307.      */
  5308.  
  5309.     if(max_line >= 1000L){         /* 1000 comes from rfc821 */
  5310.         if(body->type == TYPEOTHER){
  5311.         /*
  5312.          * Since the types not set, then we didn't find a NULL.
  5313.          * If there's no NULL, then this is likely text.  However,
  5314.          * since we can't be *completely* sure, we set it to
  5315.          * the generic type.
  5316.          */
  5317.         body->type    = TYPEAPPLICATION;
  5318.         body->subtype = cpystr("octet-stream");
  5319.         }
  5320.  
  5321.         if(new_encoding != ENCBINARY)
  5322.           /*
  5323.            * As with NULL handling, if we're told it's text, 
  5324.            * qp-encode it, else it gets base 64...
  5325.            */
  5326.           new_encoding = (body->type == TYPETEXT) ? ENC8BIT : ENCBINARY;
  5327.     }
  5328.  
  5329.     if(eight_bit_chars == 0L){
  5330.         if(body->type == TYPEOTHER)
  5331.           body->type = TYPETEXT;
  5332.  
  5333.         if(new_encoding == ENCOTHER)
  5334.           new_encoding = ENC7BIT;  /* short lines, no 8 bit */
  5335.     }
  5336.     else if(len <= 3000L || (eight_bit_chars * 100L)/len < 30L){
  5337.         /*
  5338.          * The 30% threshold is based on qp encoded readability
  5339.          * on non-MIME UA's.
  5340.          */
  5341.         can_be_ascii--;
  5342.         if(body->type == TYPEOTHER)
  5343.           body->type = TYPETEXT;
  5344.  
  5345.         if(new_encoding != ENCBINARY)
  5346.           new_encoding = ENC8BIT;  /* short lines, < 30% 8 bit chars */
  5347.     }
  5348.     else{
  5349.         can_be_ascii--;
  5350.         if(body->type == TYPEOTHER){
  5351.         body->type    = TYPEAPPLICATION;
  5352.         body->subtype = cpystr("octet-stream");
  5353.         }
  5354.  
  5355.         /*
  5356.          * Apply maximal encoding regardless of previous
  5357.          * setting.  This segment's either not text, or is 
  5358.          * unlikely to be readable with > 30% of the
  5359.          * text encoded anyway, so we might as well save space...
  5360.          */
  5361.         new_encoding = ENCBINARY;   /*  > 30% 8 bit chars */
  5362.     }
  5363.     }
  5364.  
  5365. #ifdef ENCODE_FROMS
  5366.     /* If there were From_'s at the beginning of a line or standalone dots */
  5367.     if((froms || dots) && new_encoding != ENCBINARY)
  5368.       new_encoding = ENC8BIT;
  5369. #endif
  5370.  
  5371.     /* need to set the subtype, and possibly the charset */
  5372.     if(body->type == TYPETEXT && body->subtype == NULL){
  5373.         PARAMETER *pm;
  5374.  
  5375.     body->subtype = cpystr("PLAIN");
  5376.  
  5377.     /* need to add charset */
  5378.     if(can_be_ascii > 0 || ps_global->VAR_CHAR_SET){
  5379.         if(body->parameter == NULL){
  5380.             pm = body->parameter = mail_newbody_parameter();
  5381.             pm->attribute = cpystr("charset");
  5382.         }
  5383.         else{
  5384.                 for(pm = body->parameter;
  5385.             strucmp(pm->attribute, "charset") && pm->next != NULL;
  5386.             pm = pm->next)
  5387.           ;/* find charset parameter */
  5388.  
  5389.             if(strucmp(pm->attribute, "charset") != 0){ /* add one */
  5390.             pm->next = mail_newbody_parameter();
  5391.             pm = pm->next;
  5392.             pm->attribute = cpystr("charset");
  5393.             }
  5394.         else if(pm->value)
  5395.           fs_give((void **)&pm->value);
  5396.         }
  5397.  
  5398.         pm->value = set_mime_charset(can_be_ascii > 0,
  5399.                      ps_global->VAR_CHAR_SET);
  5400.     }
  5401.     }
  5402.  
  5403.     /* need to set the charset */
  5404.     if(body->type == TYPEAPPLICATION
  5405.        && body->subtype
  5406.        && strucmp(body->subtype, "DIRECTORY") == 0
  5407.        && body->parameter
  5408.        && strucmp(body->parameter->attribute, "PROFILE") == 0
  5409.        && strucmp(body->parameter->value, "X-Email-Abook-Entry") == 0
  5410.        && (can_be_ascii > 0 || ps_global->VAR_CHAR_SET)){
  5411.         PARAMETER *pm;
  5412.  
  5413.     for(pm = body->parameter;
  5414.         strucmp(pm->attribute, "charset") && pm->next != NULL;
  5415.         pm = pm->next)
  5416.       ;/* find charset parameter */
  5417.  
  5418.     if(strucmp(pm->attribute, "charset") != 0){  /* wasn't one, add it */
  5419.         pm->next = mail_newbody_parameter();
  5420.         pm = pm->next;
  5421.         pm->attribute = cpystr("charset");
  5422.         pm->value = set_mime_charset(can_be_ascii > 0,
  5423.                      ps_global->VAR_CHAR_SET);
  5424.     }
  5425.     /* else, leave what was there */
  5426.     }
  5427.  
  5428.     if(body->encoding == ENCOTHER)
  5429.       body->encoding = new_encoding;
  5430.  
  5431. #ifndef    DOS
  5432.     fs_give((void **)&buf);
  5433. #endif
  5434.  
  5435.     if(we_cancel)
  5436.       cancel_busy_alarm(-1);
  5437. }
  5438.  
  5439.  
  5440. /*
  5441.  * set_mime_charset - assign character set for MIME body parts based
  5442.  *              on content and "downgradeability" of the provided
  5443.  *              charset.
  5444.  */
  5445. char *
  5446. set_mime_charset(ascii_ok, cs)
  5447.     int   ascii_ok;
  5448.     char *cs;
  5449. {
  5450.     char    **excl;
  5451.     static char  *non_ascii[] = {"UNICODE-1-1-UTF-7", NULL};
  5452.  
  5453.     for(excl = non_ascii; cs && *excl && strucmp(*excl, cs); excl++)
  5454.       ;
  5455.  
  5456.     return(cpystr((!cs || (ascii_ok && !*excl)) ? "US-ASCII" : cs));
  5457. }
  5458.  
  5459.  
  5460. /*
  5461.  *
  5462.  */
  5463. void
  5464. set_body_size(b)
  5465.     BODY *b;
  5466. {
  5467.     unsigned char c;
  5468.     int we_cancel = 0;
  5469.  
  5470.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  5471.     so_seek((STORE_S *)b->contents.binary, 0L, 0);
  5472.     b->size.bytes = 0L;
  5473.     while(so_readc(&c, (STORE_S *)b->contents.binary))
  5474.       b->size.bytes++;
  5475.  
  5476.     if(we_cancel)
  5477.       cancel_busy_alarm(-1);
  5478. }
  5479.  
  5480.  
  5481.  
  5482. /*
  5483.  * pine_header_line - simple wrapper around c-client call to contain
  5484.  *                    repeated code, and to write fcc if required.
  5485.  */
  5486. int
  5487. pine_header_line(tmp, field, header, text, f, s, writehdr, localcopy)
  5488.     char      *tmp;
  5489.     char      *field;
  5490.     METAENV   *header;
  5491.     char      *text;
  5492.     soutr_t    f;
  5493.     TCPSTREAM *s;
  5494.     int        writehdr, localcopy;
  5495. {
  5496.     char *p;
  5497.  
  5498.     *(p = tmp) = '\0';
  5499.     rfc822_header_line(&p, field, header ? header->env : NULL,
  5500.                rfc1522_encode(tmp_20k_buf, (unsigned char *) text,
  5501.                       ps_global->VAR_CHAR_SET));
  5502.     return(((writehdr && f) ? (*f)(s, tmp) : 1)
  5503.          && ((localcopy && local_so
  5504.          && !local_written) ? so_puts(local_so,tmp) : 1));
  5505. }
  5506.  
  5507.  
  5508. /*
  5509.  * pine_address_line - write a header field containing addresses,
  5510.  *                     one by one (so there's no buffer limit), and
  5511.  *                     wrapping where necessary.
  5512.  * Note: we use c-client functions to properly build the text string,
  5513.  *       but have to screw around with pointers to fool c-client functions
  5514.  *       into not blatting all the text into a single buffer.  Yeah, I know.
  5515.  */
  5516. int
  5517. pine_address_line(tmp, field, header, alist, f, s, writehdr, localcopy)
  5518.     char      *tmp;
  5519.     char      *field;
  5520.     METAENV   *header;
  5521.     ADDRESS   *alist;
  5522.     soutr_t    f;
  5523.     TCPSTREAM *s;
  5524.     int        writehdr, localcopy;
  5525. {
  5526.     char    *p, *delim, *ptmp, *pruned, ch;
  5527.     ADDRESS *atmp;
  5528.     int      i, count;
  5529.     int      in_group = 0, was_start_of_group = 0, fix_lcc = 0;
  5530.     static   char comma[]    = ", ";
  5531. #define    no_comma    (&comma[1])
  5532.  
  5533.     if(!alist)                /* nothing in field! */
  5534.       return(1);
  5535.  
  5536.     *(p = tmp)  = '\0';
  5537.     if(!alist->host && alist->mailbox){ /* c-client group convention */
  5538.         in_group = 1;
  5539.     was_start_of_group = 1;
  5540.     }
  5541.  
  5542.     ptmp        = alist->personal;    /* remember personal name */
  5543.     alist->personal = rfc1522_encode(tmp_20k_buf,
  5544.                      (unsigned char *) alist->personal,
  5545.                      ps_global->VAR_CHAR_SET);
  5546.     pruned        = prune_personal(alist, &ch);
  5547.     atmp            = alist->next;
  5548.     alist->next        = NULL;        /* digest only first address! */
  5549.     rfc822_address_line(&p, field, header ? header->env : NULL, alist);
  5550.     alist->next        = atmp;        /* restore pointer to next addr */
  5551.     alist->personal = ptmp;        /* in case it changed, restore name */
  5552.     if(pruned)                /* restore truncated string */
  5553.       *pruned = ch;
  5554.  
  5555.     if((count = strlen(tmp)) > 2){    /* back over CRLF */
  5556.     count -= 2;
  5557.     tmp[count] = '\0';
  5558.     }
  5559.  
  5560.     /*
  5561.      * If there is no sending_stream and we are writing the Lcc header,
  5562.      * then we are piping it to sendmail -t which expects it to be a bcc,
  5563.      * not lcc.
  5564.      *
  5565.      * When we write it to the fcc or postponed (the local_so),
  5566.      * we want it to be lcc, not bcc, so we put it back.
  5567.      */
  5568.     if(!sending_stream && writehdr && struncmp("lcc:", tmp, 4) == 0)
  5569.       fix_lcc = 1;
  5570.  
  5571.     if(writehdr && f){
  5572.     int failed;
  5573.  
  5574.     if(fix_lcc)
  5575.       tmp[0] = 'b';
  5576.     
  5577.     failed = !(*f)(s, tmp);
  5578.     if(fix_lcc)
  5579.       tmp[0] = 'L';
  5580.  
  5581.     if(failed)
  5582.       return(0);
  5583.     }
  5584.  
  5585.     if(localcopy && local_so && !local_written && !so_puts(local_so, tmp))
  5586.       return(0);
  5587.  
  5588.     for(alist = atmp; alist; alist = alist->next){
  5589.     delim = comma;
  5590.     /* account for c-client's representation of group names */
  5591.     if(in_group){
  5592.         if(!alist->host){  /* end of group */
  5593.         in_group = 0;
  5594.         was_start_of_group = 0;
  5595.         delim = no_comma;
  5596.         }
  5597.         else if(!localcopy || !local_so || local_written)
  5598.           continue;
  5599.     }
  5600.     /* start of new group, print phrase below */
  5601.         else if(!alist->host && alist->mailbox){
  5602.         in_group = 1;
  5603.         was_start_of_group = 1;
  5604.     }
  5605.  
  5606.     /* no comma before first address in group syntax */
  5607.     if(was_start_of_group && alist->host){
  5608.         delim = no_comma;
  5609.         was_start_of_group = 0;
  5610.     }
  5611.  
  5612.     /* first, write delimiter */
  5613.     if(((!in_group||was_start_of_group) && writehdr && f && !(*f)(s,delim))
  5614.        || (localcopy && local_so && !local_written
  5615.            && !so_puts(local_so, delim)))
  5616.       return(0);
  5617.  
  5618.     tmp[0]        = '\0';
  5619.     ptmp        = alist->personal; /* remember personal name */
  5620.     alist->personal = rfc1522_encode(tmp_20k_buf,
  5621.                      (unsigned char *) alist->personal,
  5622.                      ps_global->VAR_CHAR_SET);
  5623.     pruned        = prune_personal(alist, &ch);
  5624.     atmp        = alist->next;
  5625.     alist->next    = NULL;        /* tie off linked list */
  5626.     rfc822_write_address(tmp, alist);
  5627.     alist->next    = atmp;        /* restore next pointer */
  5628.     alist->personal = ptmp;        /* in case it changed, restore name */
  5629.  
  5630.     /*
  5631.      * BUG
  5632.      * With group syntax addresses we no longer have two identical
  5633.      * streams of output.  Instead, for the fcc/postpone copy we include
  5634.      * all of the addresses inside the :; of the group, and for the
  5635.      * mail we're sending we don't include them.  That means we aren't
  5636.      * correctly keeping track of the column to wrap in, below.  That is,
  5637.      * we are keeping track of the fcc copy but we aren't keeping track
  5638.      * of the regular copy.  It could result in too long or too short
  5639.      * lines.  Should almost never come up since group addresses are almost
  5640.      * never followed by other addresses in the same header, and even
  5641.      * when they are, you have to go out of your way to get the headers
  5642.      * messed up.
  5643.      */
  5644.     if(count + 2 + (i = strlen(tmp)) > 78){ /* wrap long lines... */
  5645.         count = i + 4;
  5646.         if((!in_group && writehdr && f && !(*f)(s, "\015\012    "))
  5647.            || (localcopy && local_so && !local_written &&
  5648.                        !so_puts(local_so, "\015\012    ")))
  5649.           return(0);
  5650.     }
  5651.     else
  5652.       count += i + 2;
  5653.  
  5654.     if(((!in_group || was_start_of_group) && writehdr && f && !(*f)(s, tmp))
  5655.        || (localcopy && local_so && !local_written
  5656.            && !so_puts(local_so, tmp)))
  5657.       return(0);
  5658.     }
  5659.  
  5660.     return((writehdr && f ? (*f)(s, "\015\012") : 1)
  5661.        && ((localcopy && local_so
  5662.            && !local_written) ? so_puts(local_so, "\015\012") : 1));
  5663. }
  5664.  
  5665.  
  5666. /*
  5667.  * prune_personal - function to help make sure we don't pop any c-client
  5668.  *            fixed length buffers.  Yeah, I know.
  5669.  *
  5670.  * Returns: NULL if things look ok, else pointer into some ADDRESS
  5671.  *        structure string that got truncated, and the character the
  5672.  *        new terminating NULL replaced so the string can be restored.
  5673.  */
  5674. char *
  5675. prune_personal(addr, c)
  5676.     ADDRESS *addr;
  5677.     char    *c;
  5678. {
  5679.     char *p = NULL;
  5680.  
  5681.     if(addr && addr->personal && strlen(addr->personal) > MAX_SINGLE_ADDR/2){
  5682.     p = &addr->personal[MAX_SINGLE_ADDR/2];
  5683.     *c = *p;
  5684.     *p = '\0';
  5685.     }
  5686.  
  5687.     return(p);
  5688. }
  5689.  
  5690.  
  5691. /*
  5692.  * mutated pine version of c-client's rfc822_header() function. 
  5693.  * changed to call pine-wrapped header and address functions
  5694.  * so we don't have to limit the header size to a fixed buffer.
  5695.  * This function also calls pine's body_header write function
  5696.  * because encoding is delayed until output_body() is called.
  5697.  */
  5698. int
  5699. pine_rfc822_header(header, body, f, s)
  5700.     METAENV   *header;
  5701.     BODY      *body;
  5702.     soutr_t    f;
  5703.     TCPSTREAM *s;
  5704. {
  5705.     char        tmp[MAX_SINGLE_ADDR], *p;
  5706.     PINEFIELD  *pf;
  5707.     int         j, private_header;
  5708.  
  5709.     if(header->env->remail){            /* if remailing */
  5710.     long i = strlen (header->env->remail);
  5711.     if(i > 4 && header->env->remail[i-4] == '\015')
  5712.       header->env->remail[i-2] = '\0'; /* flush extra blank line */
  5713.  
  5714.     if((f && !(*f)(s, header->env->remail)) 
  5715.       || (local_so && !local_written
  5716.           && !so_puts(local_so, header->env->remail)))
  5717.       return(0);                /* start with remail header */
  5718.     }
  5719.  
  5720.     j = 0;
  5721.     for(pf = header->sending_order[j]; pf; pf = header->sending_order[++j]){
  5722.     switch(pf->type){
  5723.       /*
  5724.        * Warning:  This is confusing.  The 2nd to last argument used to
  5725.        * be just pf->writehdr.  We want Bcc lines to be written out
  5726.        * if we are handing off to a sendmail temp file but not if we
  5727.        * are talking smtp, so bcc's writehdr is set to 0 and
  5728.        * pine_address_line was sending if writehdr OR !sending_stream.
  5729.        * That works as long as we want to write everything when
  5730.        * !sending_stream (an mta handoff to sendmail).  But then we
  5731.        * added the undisclosed recipients line which should only get
  5732.        * written if writehdr is set, and not when we pass to a
  5733.        * sendmail temp file.  So pine_address_line has been changed
  5734.        * so it bases its decision solely on the writehdr passed to it,
  5735.        * and the logic that worries about Bcc and sending_stream
  5736.        * was moved up to the caller (here) to decide when to set it.
  5737.        *
  5738.        * So we have:
  5739.        *   undisclosed recipients:;  This will just be written
  5740.        *                             if writehdr was set and not
  5741.        *                             otherwise, nothing magical.
  5742.        *** We may want to change this, because sendmail -t doesn't handle
  5743.        *** the empty group syntax well unless it has been configured to
  5744.        *** do so.  It isn't configured by default, or in any of the
  5745.        *** sendmail v8 configs.  So we may want to not write this line
  5746.        *** if we're doing an mta_handoff (!sending_stream).
  5747.        *
  5748.        *   !sending_stream (which means a handoff to a sendmail -t)
  5749.        *           bcc or lcc both set the arg so they'll get written
  5750.        *             (There is also Lcc hocus pocus in pine_address_line
  5751.        *              which converts the Lcc: to Bcc: for sendmail
  5752.        *              processing.)
  5753.        *   sending_stream (which means an smtp handoff)
  5754.        *           bcc and lcc will never have writehdr set, so
  5755.        *             will never be written (They both do have rcptto set,
  5756.        *             so they both do cause RCPT TO commands.)
  5757.        *
  5758.        *   The localcopy is independent of sending_stream and is just
  5759.        *   written if it is set for all of these.
  5760.        */
  5761.       case Address:
  5762.         if(!pine_address_line(tmp,
  5763.                   pf->name,
  5764.                   header,
  5765.                   pf->addr ? *pf->addr : NULL,
  5766.                   f,
  5767.                   s,
  5768.                   (!strucmp("bcc",pf->name ? pf->name : "")
  5769.                     || !strucmp("Lcc",pf->name ? pf->name : ""))
  5770.                        ? !sending_stream
  5771.                        : pf->writehdr,
  5772.                   pf->localcopy))
  5773.           return(0);
  5774.  
  5775.         break;
  5776.  
  5777.       case Fcc:
  5778.       case FreeText:
  5779.       case Subject:
  5780.         if(!pine_header_line(tmp, pf->name, header,
  5781.         pf->text ? *pf->text : NULL,
  5782.         f, s, pf->writehdr, pf->localcopy))
  5783.           return(0);
  5784.  
  5785.         break;
  5786.  
  5787.       default:
  5788.         q_status_message1(SM_ORDER,3,7,"Unknown header type: %s",pf->name);
  5789.         break;
  5790.     }
  5791.     }
  5792.  
  5793.  
  5794. #if    (defined(DOS) || defined(OS2)) && !defined(NOAUTH)
  5795.     /*
  5796.      * Add comforting "X-" header line indicating what sort of 
  5797.      * authenticity the receiver can expect...
  5798.      */
  5799.     {
  5800.      NETMBX         netmbox;
  5801.      char         sstring[MAILTMPLEN], *label;    /* place to write  */
  5802.      MAILSTREAM *stream;
  5803.  
  5804.      if(((stream = ps_global->inbox_stream)
  5805.          && mail_valid_net_parse(stream->mailbox, &netmbox)
  5806.          && !netmbox.anoflag)
  5807.         || ((stream = ps_global->mail_stream) != ps_global->inbox_stream
  5808.         && mail_valid_net_parse(stream->mailbox, &netmbox)
  5809.         && !netmbox.anoflag)){
  5810.          char  last_char = netmbox.host[strlen(netmbox.host) - 1],
  5811.           *user = !strncmp(stream->dtb->name, "imap", 4)
  5812.                 ? imap_user(stream)
  5813.                 : cached_user_name(netmbox.mailbox);
  5814.          sprintf(sstring, "%s@%s%s%s", user ? user : "NULL", 
  5815.              isdigit((unsigned char)last_char) ? "[" : "",
  5816.              netmbox.host,
  5817.              isdigit((unsigned char) last_char) ? "]" : "");
  5818.          label = "X-X-Sender";        /* Jeez. */
  5819.          if(F_ON(F_USE_SENDER_NOT_X,ps_global))
  5820.            label += 4;
  5821.      }
  5822.      else{
  5823.          strcpy(sstring,"UNAuthenticated Sender");
  5824.          label = "X-Warning";
  5825.      }
  5826.  
  5827.      if(!pine_header_line(tmp, label, header, sstring, f, s, 1, 1))
  5828.        return(0);
  5829.      }
  5830. #endif
  5831.  
  5832.     if(body && !header->env->remail){    /* not if remail or no body */
  5833.     if((f && !(*f)(s, MIME_VER))
  5834.        || (local_so && !local_written && !so_puts(local_so, MIME_VER)))
  5835.       return(0);
  5836.  
  5837.     *(p = tmp) = '\0';
  5838.     pine_write_body_header(&p, body);
  5839.  
  5840.     if((f && !(*f)(s, tmp)) 
  5841.        || (local_so && !local_written && !so_puts(local_so, tmp)))
  5842.       return(0);
  5843.     }
  5844.     else{                    /* write terminating newline */
  5845.     if((f && !(*f)(s, "\015\012"))
  5846.        || (local_so && !local_written && !so_puts(local_so, "\015\012")))
  5847.       return(0);
  5848.     }
  5849.  
  5850.     return(1);
  5851. }
  5852.  
  5853.  
  5854. /*
  5855.  * since encoding happens on the way out the door, this is basically
  5856.  * just needed to handle TYPEMULTIPART
  5857.  */
  5858. void
  5859. pine_encode_body (body)
  5860.     BODY *body;
  5861. {
  5862.   void *f;
  5863.   PART *part;
  5864.   dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
  5865.   if (body) switch (body->type) {
  5866.   case TYPEMULTIPART:        /* multi-part */
  5867.     if (!body->parameter) {    /* cookie not set up yet? */
  5868.       char tmp[MAILTMPLEN];    /* make cookie not in BASE64 or QUOTEPRINT*/
  5869.       sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
  5870.            getpid ());
  5871.       body->parameter = mail_newbody_parameter ();
  5872.       body->parameter->attribute = cpystr ("BOUNDARY");
  5873.       body->parameter->value = cpystr (tmp);
  5874.     }
  5875.     part = body->contents.part;    /* encode body parts */
  5876.     do pine_encode_body (&part->body);
  5877.     while (part = part->next);    /* until done */
  5878.     break;
  5879. /* case MESSAGE:    */    /* here for documentation */
  5880.     /* Encapsulated messages are always treated as text objects at this point.
  5881.        This means that you must replace body->contents.msg with
  5882.        body->contents.text, which probably involves copying
  5883.        body->contents.msg.text to body->contents.text */
  5884.   default:            /* all else has some encoding */
  5885.     /*
  5886.      * but we'll delay encoding it until the message is on the way
  5887.      * into the mail slot...
  5888.      */
  5889.     break;
  5890.   }
  5891. }
  5892.  
  5893.  
  5894. /*
  5895.  * post_rfc822_output - cloak for pine's 822 output routine.  Since
  5896.  *            we can't pass opaque envelope thru c-client posting
  5897.  *            logic, we need to wrap the real output inside
  5898.  *            something that c-client knows how to call.
  5899.  */
  5900. int
  5901. post_rfc822_output (tmp, env, body, f, s)
  5902.     char      *tmp;
  5903.     ENVELOPE  *env;
  5904.     BODY      *body;
  5905.     soutr_t    f;
  5906.     TCPSTREAM *s;
  5907. {
  5908.     return(pine_rfc822_output(send_header, body, f, s));
  5909. }
  5910.  
  5911.  
  5912. /*
  5913.  * pine_rfc822_output - pine's version of c-client call.  Necessary here
  5914.  *            since we're not using its structures as intended!
  5915.  */
  5916. int
  5917. pine_rfc822_output(header, body, f, s)
  5918.     METAENV   *header;
  5919.     BODY      *body;
  5920.     soutr_t    f;
  5921.     TCPSTREAM *s;
  5922. {
  5923.     int we_cancel = 0;
  5924.     int retval;
  5925.  
  5926.     dprint(4, (debugfile, "-- pine_rfc822_output\n"));
  5927.  
  5928.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  5929.     pine_encode_body(body);        /* encode body as necessary */
  5930.     /* build and output RFC822 header, output body */
  5931.     retval = pine_rfc822_header(header, body, f, s)
  5932.        && (body ? pine_rfc822_output_body(body, f, s) : 1);
  5933.  
  5934.     if(we_cancel)
  5935.       cancel_busy_alarm(-1);
  5936.  
  5937.     return(retval);
  5938. }
  5939.  
  5940.  
  5941. /*
  5942.  * Local globals pine's body output routine needs
  5943.  */
  5944. static soutr_t    l_f;
  5945. static TCPSTREAM *l_stream;
  5946. static unsigned   c_in_buf = 0;
  5947.  
  5948. /*
  5949.  * def to make our pipe write's more friendly
  5950.  */
  5951. #ifdef    PIPE_MAX
  5952. #if    PIPE_MAX > 20000
  5953. #undef    PIPE_MAX
  5954. #endif
  5955. #endif
  5956.  
  5957. #ifndef    PIPE_MAX
  5958. #define    PIPE_MAX    1024
  5959. #endif
  5960.  
  5961.  
  5962. /*
  5963.  * l_flust_net - empties gf_io terminal function's buffer
  5964.  */
  5965. int
  5966. l_flush_net(force)
  5967.     int force;
  5968. {
  5969.     if(c_in_buf){
  5970.     char *p = &tmp_20k_buf[0], *lp = NULL, c = '\0';
  5971.  
  5972.     tmp_20k_buf[c_in_buf] = '\0';
  5973.     if(!force){
  5974.         /*
  5975.          * The start of each write is expected to be the start of a
  5976.          * "record" (i.e., a CRLF terminated line).  Make sure that is true
  5977.          * else we might screw up SMTP dot quoting...
  5978.          */
  5979.         for(p = tmp_20k_buf, lp = NULL;
  5980.         p = strstr(p, "\015\012");
  5981.         lp = (p += 2))
  5982.           ;
  5983.  
  5984.  
  5985.         if(!lp && c_in_buf > 2)            /* no CRLF! */
  5986.           for(p = &tmp_20k_buf[c_in_buf] - 2;
  5987.           p > &tmp_20k_buf[0] && *p == '.';
  5988.           p--)                    /* find last non-dot */
  5989.         ;
  5990.  
  5991.         if(lp && *lp){                /* snippet remains */
  5992.         c = *lp;
  5993.         *lp = '\0';
  5994.         }
  5995.     }
  5996.  
  5997.     if((l_f && !(*l_f)(l_stream, tmp_20k_buf))
  5998.        || (local_so && !local_written && !so_puts(local_so, tmp_20k_buf)))
  5999.       return(0);
  6000.  
  6001.     c_in_buf = 0;
  6002.     if(lp && (*lp = c))                /* Shift text left? */
  6003.       while(tmp_20k_buf[c_in_buf] = *lp)
  6004.         c_in_buf++, lp++;
  6005.     }
  6006.  
  6007.     return(1);
  6008. }
  6009.  
  6010.  
  6011. /*
  6012.  * l_putc - gf_io terminal function that calls smtp's soutr_t function.
  6013.  *
  6014.  */
  6015. int
  6016. l_putc(c)
  6017.     int c;
  6018. {
  6019.     tmp_20k_buf[c_in_buf++] = (char) c;
  6020.     return((c_in_buf >= PIPE_MAX) ? l_flush_net(FALSE) : TRUE);
  6021. }
  6022.  
  6023.  
  6024.  
  6025. /*
  6026.  * pine_rfc822_output_body - pine's version of c-client call.  Again, 
  6027.  *                necessary since c-client doesn't know about how
  6028.  *                we're treating attachments
  6029.  */
  6030. long
  6031. pine_rfc822_output_body(body, f, s)
  6032.     BODY *body;
  6033.     soutr_t f;
  6034.     TCPSTREAM *s;
  6035. {
  6036.     PART *part;
  6037.     PARAMETER *param;
  6038.     char *cookie = NIL, *t, *encode_error;
  6039.     char tmp[MAILTMPLEN];
  6040.     gf_io_t            gc;
  6041. #if defined(DOS) || defined(OS2)
  6042.     extern unsigned char  *xlate_from_codepage;
  6043. #endif
  6044.  
  6045.     dprint(4, (debugfile, "-- pine_rfc822_output_body: %d\n",
  6046.            body ? body->type : 0));
  6047.     if(body->type == TYPEMULTIPART) {   /* multipart gets special handling */
  6048.     part = body->contents.part;    /* first body part */
  6049.                     /* find cookie */
  6050.     for (param = body->parameter; param && !cookie; param = param->next)
  6051.       if (!strcmp (param->attribute,"BOUNDARY")) cookie = param->value;
  6052.     if (!cookie) cookie = "-";    /* yucky default */
  6053.  
  6054.     /*
  6055.      * Output a bit of text before the first multipart delimiter
  6056.      * to warn unsuspecting users of non-mime-aware ua's that
  6057.      * they should expect weirdness...
  6058.      */
  6059.     if(f && !(*f)(s, "  This message is in MIME format.  The first part should be readable text,\015\012  while the remaining parts are likely unreadable without MIME-aware tools.\015\012  Send mail to mime@docserver.cac.washington.edu for more info.\015\012\015\012"))
  6060.       return(0);
  6061.  
  6062.     do {                /* for each part */
  6063.                     /* build cookie */
  6064.         sprintf (t = tmp,"--%s\015\012",cookie);
  6065.                     /* append mini-header */
  6066.         pine_write_body_header (&t,&part->body);
  6067.                 /* output cookie, mini-header, and contents */
  6068.         if(local_so && !local_written)
  6069.           so_puts(local_so, tmp);
  6070.  
  6071.         if (!((f ? (*f) (s,tmp) : 1)
  6072.           && pine_rfc822_output_body (&part->body,f,s)))
  6073.           return(0);
  6074.     } while (part = part->next);    /* until done */
  6075.                     /* output trailing cookie */
  6076.     sprintf (t = tmp,"--%s--",cookie);
  6077.     if(local_so && !local_written){
  6078.         so_puts(local_so, t);
  6079.         so_puts(local_so, "\015\012");
  6080.     }
  6081.  
  6082.     return(f ? ((*f) (s,t) && (*f) (s,"\015\012")) : 1);
  6083.     }
  6084.  
  6085.     l_f      = f;            /* set up for writing chars...  */
  6086.     l_stream = s;            /* out other end of pipe...     */
  6087.     gf_filter_init();
  6088.     dprint(4, (debugfile, "-- pine_rfc822_output_body: segment %ld bytes\n",
  6089.            body->size.bytes));
  6090.  
  6091.     if(body->contents.binary)
  6092.       gf_set_so_readc(&gc, (STORE_S *)body->contents.binary);
  6093.     else
  6094.       return(1);
  6095.  
  6096.     so_seek((STORE_S *)body->contents.binary, 0L, 0);
  6097.  
  6098.     if(body->type != TYPEMESSAGE){     /* NOT encapsulated message */
  6099.     /*
  6100.      * Convert text pieces to canonical form
  6101.      * BEFORE applying any encoding (rfc1341: appendix G)...
  6102.      */
  6103.     if(body->type == TYPETEXT){
  6104.         gf_link_filter(gf_local_nvtnl);
  6105.  
  6106. #if defined(DOS) || defined(OS2)
  6107.         if(xlate_from_codepage){
  6108.         gf_translate_opt(xlate_from_codepage, 256);
  6109.         gf_link_filter(gf_translate);
  6110.         }
  6111. #endif
  6112.     }
  6113.  
  6114.     switch (body->encoding) {    /* all else needs filtering */
  6115.       case ENC8BIT:            /* encode 8BIT into QUOTED-PRINTABLE */
  6116.         gf_link_filter(gf_8bit_qp);
  6117.         break;
  6118.  
  6119.       case ENCBINARY:        /* encode binary into BASE64 */
  6120.         gf_link_filter(gf_binary_b64);
  6121.         break;
  6122.  
  6123.       default:            /* otherwise text */
  6124.         break;
  6125.     }
  6126.     }
  6127.  
  6128.     if(encode_error = gf_pipe(gc, l_putc)){ /* shove body part down pipe */
  6129.     q_status_message1(SM_ORDER | SM_DING, 3, 4,
  6130.               "Encoding Error \"%s\"", encode_error);
  6131.     display_message('x');
  6132.     return(0);
  6133.     }
  6134.     else if(!l_flush_net(TRUE)){
  6135.     return(0);
  6136.     }
  6137.  
  6138.     send_bytes_sent += gf_bytes_piped();
  6139.     so_release((STORE_S *)body->contents.binary);
  6140.  
  6141.     if(local_so && !local_written)
  6142.       so_puts(local_so, "\015\012");
  6143.  
  6144.     return(f ? (*f)(s, "\015\012") : 1);    /* output final stuff */
  6145. }
  6146.  
  6147.  
  6148. /*
  6149.  * pine_write_body_header - another c-client clone.  This time only
  6150.  *                          so the final encoding labels get set 
  6151.  *                          correctly since it hasn't happened yet.
  6152.  */
  6153. void
  6154. pine_write_body_header(dst, body)
  6155.     char **dst;
  6156.     BODY  *body;
  6157. {
  6158.     char *s;
  6159.     PARAMETER *param = body->parameter;
  6160.     extern const char *tspecials;
  6161.  
  6162.     sprintf (*dst += strlen (*dst),"Content-Type: %s",body_types[body->type]);
  6163.     s = body->subtype ? body->subtype : rfc822_default_subtype (body->type);
  6164.     sprintf (*dst += strlen (*dst),"/%s",s);
  6165.     if (param) do {
  6166.     sprintf (*dst += strlen (*dst),"; %s=",param->attribute);
  6167.     rfc822_cat (*dst,param->value,tspecials);
  6168.     } while (param = param->next);
  6169.     else if (body->type == TYPETEXT) strcat (*dst,"; charset=US-ASCII");
  6170.     strcpy (*dst += strlen (*dst),"\015\012");
  6171.     if (body->encoding)        /* note: encoding 7BIT never output! */
  6172.       sprintf (*dst += strlen (*dst), "Content-Transfer-Encoding: %s\015\012",
  6173.            body_encodings[(body->encoding == ENCBINARY)
  6174.                 ? ENCBASE64
  6175.                 : (body->encoding == ENC8BIT)
  6176.                     ? ENCQUOTEDPRINTABLE
  6177.                     : (body->encoding <= ENCMAX)
  6178.                     ? body->encoding : ENCOTHER]);
  6179.     if (body->id) sprintf (*dst += strlen (*dst),"Content-ID: %s\015\012",
  6180.                body->id);
  6181.     if (body->description)
  6182.       sprintf (*dst += strlen (*dst),"Content-Description: %s\015\012",
  6183.            body->description);
  6184.     strcat (*dst,"\015\012");    /* write terminating blank line */
  6185. }
  6186.  
  6187.  
  6188. /*
  6189.  * pine_free_body - yet another c-client clone just so the body gets
  6190.  *                  free'd appropriately
  6191.  */
  6192. void
  6193. pine_free_body(body)
  6194.     BODY **body;
  6195. {
  6196.     if (*body) {            /* only free if exists */
  6197.     pine_free_body_data (*body);    /* free its data */
  6198.     fs_give ((void **) body);    /* return body to free storage */
  6199.     }
  6200. }
  6201.  
  6202.  
  6203. /* 
  6204.  * pine_free_body_data - not just releasing strings anymore!
  6205.  */
  6206. void
  6207. pine_free_body_data(body)
  6208.     BODY *body;
  6209. {
  6210.     if (body->subtype) fs_give ((void **) &body->subtype);
  6211.     mail_free_body_parameter (&body->parameter);
  6212.     if (body->id) fs_give ((void **) &body->id);
  6213.     if (body->description) fs_give ((void **) &body->description);
  6214.     if(body->type == TYPEMULTIPART){
  6215.     pine_free_body_part (&body->contents.part);
  6216.     }
  6217.     else if(body->contents.binary){
  6218.     so_give((STORE_S **)&body->contents.binary);
  6219.     body->contents.binary = NULL;
  6220.     }
  6221. }
  6222.  
  6223.  
  6224. /*
  6225.  * pine_free_body_part - c-client clone to call the right routines
  6226.  *             for cleaning up.
  6227.  */
  6228. void
  6229. pine_free_body_part(part)
  6230.     PART **part;
  6231. {
  6232.     if (*part) {        /* only free if exists */
  6233.     pine_free_body_data (&(*part)->body);
  6234.                 /* run down the list as necessary */
  6235.     pine_free_body_part (&(*part)->next);
  6236.     fs_give ((void **) part); /* return body part to free storage */
  6237.     }
  6238. }
  6239.  
  6240.  
  6241. /*
  6242.  *
  6243.  */
  6244. int
  6245. sent_percent()
  6246. {
  6247.     int i = (int) (((send_bytes_sent + gf_bytes_piped()) * 100)
  6248.                             / send_bytes_to_send);
  6249.     return(min(i, 100));
  6250. }
  6251.  
  6252.  
  6253.  
  6254. /*
  6255.  *
  6256.  */
  6257. long
  6258. send_body_size(body)
  6259.     BODY *body;
  6260. {
  6261.     long  l = 0L;
  6262.     PART *part;
  6263.  
  6264.     if(body->type == TYPEMULTIPART) {   /* multipart gets special handling */
  6265.     part = body->contents.part;    /* first body part */
  6266.     do                /* for each part */
  6267.       l += send_body_size(&part->body);
  6268.     while (part = part->next);    /* until done */
  6269.     return(l);
  6270.     }
  6271.  
  6272.     return(l + body->size.bytes);
  6273. }
  6274.  
  6275.  
  6276.  
  6277. /*
  6278.  * pine_smtp_verbose - pine's contribution to the smtp stream.  Return
  6279.  *               TRUE for any positive reply code to our "VERBose"
  6280.  *               request.
  6281.  *
  6282.  *    NOTE: at worst, this command may cause the SMTP connection to get
  6283.  *          nuked.  Modern sendmail's recognize it, and possibly other
  6284.  *          SMTP implementations (the "ON" arg is for PMDF).  What's
  6285.  *          more, if it works, what's returned (sendmail uses reply code
  6286.  *          050, but we'll accept anything less than 100) may not get 
  6287.  *          recognized, and if it does the accompanying text will likely
  6288.  *          vary from server to server.
  6289.  */
  6290. long
  6291. pine_smtp_verbose(stream)
  6292.     SMTPSTREAM *stream;
  6293. {
  6294.     /* any 2xx reply to this is acceptable */
  6295.     return(smtp_send(stream,"VERB","ON")/100L == 2L);
  6296. }
  6297.  
  6298.  
  6299.  
  6300. /*
  6301.  * pine_smtp_verbose_out - write 
  6302.  */
  6303. void
  6304. pine_smtp_verbose_out(s)
  6305.     char *s;
  6306. {
  6307.     if(verbose_send_output && s){
  6308.     char *p, last = '\0';
  6309.  
  6310.     for(p = s; *p; p++)
  6311.       if(*p == '\015')
  6312.         *p = ' ';
  6313.       else
  6314.         last = *p;
  6315.  
  6316.     fputs(s, verbose_send_output);
  6317.     if(last != '\012')
  6318.       fputc('\n', verbose_send_output);
  6319.     }
  6320.  
  6321. }
  6322.  
  6323.  
  6324.  
  6325. /*----------------------------------------------------------------------
  6326.       Post news via NNTP or inews
  6327.  
  6328. Args: env -- envelope of message to post
  6329.        body -- body of message to post
  6330.  
  6331. Returns: -1 if failed or cancelled, 1 if succeeded
  6332.  
  6333. WARNING: This call function has the side effect of writing the message
  6334.     to the local_so object.   
  6335.   ----*/
  6336. news_poster(header, body)
  6337.      METAENV *header;
  6338.      BODY    *body;
  6339. {
  6340.     char *error_mess, error_buf[100];
  6341.     int      rv, we_cancel = 0;
  6342.     void *orig_822_output;
  6343.     BODY *bp = NULL;
  6344.  
  6345.     we_cancel = busy_alarm(1, "Posting news", NULL, 1);
  6346.  
  6347.     dprint(4, (debugfile, "Posting: [%s]\n", header->env->newsgroups));
  6348.  
  6349.     if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
  6350.        && ps_global->VAR_NNTP_SERVER[0][0]){
  6351.        /*---------- NNTP server defined ----------*/
  6352.         error_mess              = NULL;
  6353.  
  6354.     /*
  6355.      * Install our rfc822 output routine 
  6356.      */
  6357.     orig_822_output = mail_parameters(NULL, GET_RFC822OUTPUT, NULL);
  6358.     (void)mail_parameters(NULL,SET_RFC822OUTPUT,(void *)post_rfc822_output);
  6359.  
  6360.         ps_global->noshow_error = 1;
  6361. #ifdef DEBUG
  6362.         sending_stream = nntp_open(ps_global->VAR_NNTP_SERVER, debug);
  6363. #else
  6364.         sending_stream = nntp_open(ps_global->VAR_NNTP_SERVER, 0L);
  6365. #endif
  6366.         ps_global->noshow_error = 0;
  6367.  
  6368.         if(sending_stream != NULL) {
  6369.         /*
  6370.          * Fake that we've got clearance from the transport agent
  6371.          * for 8bit transport for the benefit of our output routines...
  6372.          */
  6373.         if(F_ON(F_ENABLE_8BIT_NNTP, ps_global)
  6374.            && (bp = first_text_8bit(body))){
  6375.         int i;
  6376.  
  6377.         for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++)
  6378.           ;
  6379.  
  6380.         if(i > ENCMAX){        /* no empty encoding slots! */
  6381.             bp = NULL;
  6382.         }
  6383.         else {
  6384.             body_encodings[i] = body_encodings[ENC8BIT];
  6385.             bp->encoding = (unsigned short) i;
  6386.         }
  6387.         }
  6388.  
  6389.         /*
  6390.          * Set global header pointer so we can get at it later...
  6391.          */
  6392.         send_header = header;
  6393.  
  6394.             if(nntp_mail(sending_stream, header->env, body) == 0)
  6395.           sprintf(error_mess = error_buf, "Error posting message: %.60s",
  6396.               sending_stream->reply);
  6397.  
  6398.             smtp_close(sending_stream);
  6399.         sending_stream = NULL;
  6400.         if(F_ON(F_ENABLE_8BIT_NNTP, ps_global) && bp)
  6401.           body_encodings[bp->encoding] = NULL;
  6402.  
  6403.         } else {
  6404.             /*---- Open of NNTP connection failed ------ */
  6405.             sprintf(error_mess = error_buf,
  6406.             "Error connecting to news server: %.60s",
  6407.                     ps_global->c_client_error);
  6408.             dprint(1, (debugfile, error_buf));
  6409.         }
  6410.  
  6411.     (void) mail_parameters (NULL, SET_RFC822OUTPUT, orig_822_output);
  6412.     } else {
  6413.         /*----- Post via local mechanism -------*/
  6414.         error_mess = post_handoff(header, body, error_buf);
  6415.     }
  6416.  
  6417.     if(we_cancel)
  6418.       cancel_busy_alarm(0);
  6419.  
  6420.     if(error_mess){
  6421.     if(local_so && !local_written)
  6422.       so_give(&local_so);    /* clean up any fcc data */
  6423.  
  6424.         q_status_message(SM_ORDER | SM_DING, 4, 5, error_mess);
  6425.         return(-1);
  6426.     }
  6427.  
  6428.     local_written = 1;
  6429.     return(1);
  6430. }
  6431.  
  6432.  
  6433.  
  6434. /*
  6435.  * view_as_rich - set the rich_header flag
  6436.  *
  6437.  *         name  - name of the header field
  6438.  *         deflt - default value to return if user didn't set it
  6439.  *
  6440.  *         Note: if the user tries to turn them all off with "", then
  6441.  *         we take that to mean default, since otherwise there is no
  6442.  *         way to get to the headers.
  6443.  */
  6444. int
  6445. view_as_rich(name, deflt)
  6446. char *name;
  6447. int deflt;
  6448. {
  6449.     char **p;
  6450.     char *q;
  6451.  
  6452.     p = ps_global->VAR_COMP_HDRS;
  6453.  
  6454.     if(p && *p && **p){
  6455.         for(; (q = *p) != NULL; p++){
  6456.         if(!struncmp(q, name, strlen(name)))
  6457.           return 0; /* 0 means we *do* view it by default */
  6458.     }
  6459.  
  6460.         return 1; /* 1 means it starts out hidden */
  6461.     }
  6462.     return(deflt);
  6463. }
  6464.  
  6465.  
  6466. /*
  6467.  * is_a_forbidden_hdr - is this name a "forbidden" header?
  6468.  *
  6469.  *          name - the header name to check
  6470.  *  We don't allow user to change these.
  6471.  */
  6472. int
  6473. is_a_forbidden_hdr(name)
  6474. char *name;
  6475. {
  6476.     char **p;
  6477.     static char *forbidden_headers[] = {
  6478.     "sender",
  6479.     "x-sender",
  6480.     "date",
  6481.     "received",
  6482.     "message-id",
  6483.     "in-reply-to",
  6484.     "path",
  6485.     "resent-message-id",
  6486.     "resent-message-date",
  6487.     "resent-message-from",
  6488.     "resent-message-sender",
  6489.     "resent-message-to",
  6490.     "resent-message-cc",
  6491.     "resent-message-reply-to",
  6492.     NULL
  6493.     };
  6494.  
  6495.     for(p = forbidden_headers; *p; p++)
  6496.       if(!strucmp(name, *p))
  6497.     break;
  6498.  
  6499.     return((*p) ? 1 : 0);
  6500. }
  6501.  
  6502.  
  6503. /*
  6504.  * hdr_has_custom_value - is there a custom value for this header?
  6505.  *
  6506.  *          hdr - the header name to check
  6507.  *  Returns 1 if there is a custom value, 0 otherwise.
  6508.  */
  6509. int
  6510. hdr_has_custom_value(hdr)
  6511. char *hdr;
  6512. {
  6513.     char **p;
  6514.     char  *q, *t, *name;
  6515.     char   save;
  6516.  
  6517.     if(ps_global->VAR_CUSTOM_HDRS){
  6518.         for(p = ps_global->VAR_CUSTOM_HDRS; (q = *p) != NULL; p++){
  6519.  
  6520.         if(q[0]){
  6521.  
  6522.         /* remove leading whitespace */
  6523.             for(name = q; *name && isspace((unsigned char)*name); name++)
  6524.               ;/* do nothing */
  6525.         
  6526.         if(!*name)
  6527.           continue;
  6528.  
  6529.         /* look for colon or space or end */
  6530.             for(t = name;
  6531.             *t && !isspace((unsigned char)*t) && *t != ':'; t++)
  6532.               ;/* do nothing */
  6533.  
  6534.         save = *t;
  6535.         *t = '\0';
  6536.  
  6537.         if(strucmp(name, hdr) == 0){
  6538.             *t = save;
  6539.             return 1;
  6540.         }
  6541.  
  6542.         *t = save;
  6543.         }
  6544.     }
  6545.     }
  6546.  
  6547.     return 0;
  6548. }
  6549. /*
  6550.  * count_custom_hdrs - returns number of custom headers defined by user
  6551.  */
  6552. int
  6553. count_custom_hdrs()
  6554. {
  6555.     char **p;
  6556.     char  *q     = NULL;
  6557.     char  *name;
  6558.     char  *t;
  6559.     char   save;
  6560.     int    ret   = 0;
  6561.  
  6562.     if(ps_global->VAR_CUSTOM_HDRS){
  6563.         for(p = ps_global->VAR_CUSTOM_HDRS; (q = *p) != NULL; p++){
  6564.         if(q[0]){
  6565.         /* remove leading whitespace */
  6566.             for(name = q; *name && isspace((unsigned char)*name); name++)
  6567.               ;/* do nothing */
  6568.         
  6569.         /* look for colon or space or end */
  6570.             for(t = name;
  6571.             *t && !isspace((unsigned char)*t) && *t != ':'; t++)
  6572.               ;/* do nothing */
  6573.  
  6574.         save = *t;
  6575.         *t = '\0';
  6576.         if(!is_a_std_hdr(name) && !is_a_forbidden_hdr(name))
  6577.           ret++;
  6578.  
  6579.         *t = save;
  6580.         }
  6581.     }
  6582.     }
  6583.  
  6584.     return(ret);
  6585. }
  6586.  
  6587.  
  6588. /*
  6589.  * set_default_hdrval - put the user's default value for this header
  6590.  *                      into pf->textbuf.
  6591.  */
  6592. void
  6593. set_default_hdrval(pf)
  6594. PINEFIELD *pf;
  6595. {
  6596.     char       **p = 0;
  6597.     char        *q = 0;
  6598.     char        *t;
  6599.     char        *name;
  6600.     char        *value;
  6601.     char         save;
  6602.  
  6603.     if(!pf || !pf->name){
  6604.     q_status_message(SM_ORDER,3,7,"Internal error setting default header");
  6605.     return;
  6606.     }
  6607.  
  6608.     pf->textbuf = NULL;
  6609.  
  6610.     if(ps_global->VAR_CUSTOM_HDRS){
  6611.         for(p = ps_global->VAR_CUSTOM_HDRS; (q = *p) != NULL; p++){
  6612.  
  6613.         if(q[0]){
  6614.  
  6615.         /* remove leading whitespace */
  6616.             for(name = q; *name && isspace((unsigned char)*name); name++)
  6617.               ;/* do nothing */
  6618.         
  6619.         if(!*name)
  6620.           continue;
  6621.  
  6622.         /* look for colon or space or end */
  6623.             for(t = name;
  6624.             *t && !isspace((unsigned char)*t) && *t != ':'; t++)
  6625.               ;/* do nothing */
  6626.  
  6627.         save = *t;
  6628.         *t = '\0';
  6629.  
  6630.         if(strucmp(name, pf->name) != 0){
  6631.             *t = save;
  6632.             continue;
  6633.         }
  6634.  
  6635.         /* turn on editing */
  6636.         if(strucmp(name, "From") == 0
  6637.           || strucmp(name, "Reply-To") == 0)
  6638.           pf->canedit = 1;
  6639.  
  6640.         *t = save;
  6641.  
  6642.         /* remove space between name and colon */
  6643.             for(value = t;
  6644.             *value && isspace((unsigned char)*value); value++)
  6645.               ;/* do nothing */
  6646.  
  6647.         if(*value && *value == ':'){
  6648.             /* remove leading whitespace from default value */
  6649.             for(++value;
  6650.             *value && isspace((unsigned char)*value); value++)
  6651.               ;/* do nothing */
  6652.  
  6653.             pf->textbuf = cpystr(value);
  6654.         }
  6655.  
  6656.         break;
  6657.         }
  6658.     }
  6659.     }
  6660.  
  6661.     if(!pf->textbuf)
  6662.       pf->textbuf = cpystr("");
  6663. }
  6664.  
  6665.  
  6666. /*
  6667.  * is_a_std_hdr - is this name a "standard" header?
  6668.  *
  6669.  *          name - the header name to check
  6670.  */
  6671. int
  6672. is_a_std_hdr(name)
  6673. char *name;
  6674. {
  6675.     int    i;
  6676.     /* how many standard headers are there? */
  6677.     int fixed_cnt = (sizeof(pf_template)/sizeof(pf_template[0])) - 1;
  6678.  
  6679.     /* check to see if this is a standard header */
  6680.     for(i = 0; i < fixed_cnt; i++)
  6681.       if(!strucmp(name, pf_template[i].name))
  6682.     break;
  6683.  
  6684.     return((i < fixed_cnt) ? 1 : 0);
  6685. }
  6686.  
  6687.  
  6688. /*
  6689.  * customized_hdr_setup - setup the PINEFIELDS for all the customized headers
  6690.  *                    Allocates space for each name and addr ptr.
  6691.  *                    Allocates space for default in textbuf, even if empty.
  6692.  *
  6693.  *              head - the first PINEFIELD to fill in
  6694.  */
  6695. void
  6696. customized_hdr_setup(head)
  6697. PINEFIELD *head;
  6698. {
  6699.     char **p, *q, *t, *name, *value, save;
  6700.     int forbid;
  6701.     PINEFIELD *pf;
  6702.  
  6703.     pf = head;
  6704.  
  6705.     if(ps_global->VAR_CUSTOM_HDRS){
  6706.         for(p = ps_global->VAR_CUSTOM_HDRS; (q = *p) != NULL; p++){
  6707.  
  6708.         if(q[0]){
  6709.  
  6710.         /* remove leading whitespace */
  6711.             for(name = q; *name && isspace((unsigned char)*name); name++)
  6712.               ;/* do nothing */
  6713.         
  6714.         if(!*name)
  6715.           continue;
  6716.  
  6717.         /* look for colon or space or end */
  6718.             for(t = name;
  6719.             *t && !isspace((unsigned char)*t) && *t != ':'; t++)
  6720.               ;/* do nothing */
  6721.  
  6722.         save = *t;
  6723.         *t = '\0';
  6724.  
  6725.         /*
  6726.          * The same pinerc variable is used to customize standard
  6727.          * headers, so skip them here.  Also, don't allow
  6728.          * any of the forbidden headers.
  6729.          */
  6730.         forbid = 0;
  6731.         if(is_a_std_hdr(name) || (forbid=is_a_forbidden_hdr(name))){
  6732.             if(forbid)
  6733.               q_status_message1(SM_ORDER, 3, 3,
  6734.                 "Not allowed to change header \"%s\"", name);
  6735.  
  6736.             *t = save;
  6737.             continue;
  6738.         }
  6739.  
  6740.         pf->name = cpystr(name);
  6741.         pf->type = FreeText;
  6742.         pf->next = pf+1;
  6743.  
  6744. #ifdef OLDWAY
  6745.         /*
  6746.          * Some mailers apparently break if we change
  6747.          * user@domain into Fred <user@domain> for return-receipt-to,
  6748.          * so we'll just call this a FreeText field, too.
  6749.          */
  6750.         /*
  6751.          * For now, all custom headers are FreeText except for
  6752.          * this one that we happen to know about.  We might
  6753.          * have to add some syntax to the config option so that
  6754.          * people can tell us their custom header takes addresses.
  6755.          */
  6756.         if(!strucmp(pf->name, "Return-Receipt-to")){
  6757.             pf->type = Address;
  6758.              pf->addr = (ADDRESS **)fs_get(sizeof(ADDRESS *));
  6759.             *pf->addr = (ADDRESS *)NULL;
  6760.         }
  6761. #endif /* OLDWAY */
  6762.  
  6763.         *t = save;
  6764.  
  6765.         /* remove space between name and colon */
  6766.             for(value = t;
  6767.             *value && isspace((unsigned char)*value); value++)
  6768.               ;/* do nothing */
  6769.  
  6770.         /* give them an alloc'd default, even if empty */
  6771.         if(!*value || (*value && *value != ':'))
  6772.           pf->textbuf = cpystr("");
  6773.         else{
  6774.             /* remove leading whitespace from default value */
  6775.             for(++value;
  6776.             *value && isspace((unsigned char)*value); value++)
  6777.               ;/* do nothing */
  6778.  
  6779.             pf->textbuf = cpystr(value);
  6780.         }
  6781.  
  6782.         pf++;
  6783.         }
  6784.     }
  6785.     }
  6786.  
  6787.     /* fix last next pointer */
  6788.     if(pf != head)
  6789.       (pf-1)->next = NULL;
  6790. }
  6791.  
  6792.  
  6793. /*
  6794.  * get_dflt_custom_hdrs - allocate PINEFIELDS for custom headers
  6795.  *                        fill in the defaults
  6796.  */
  6797. PINEFIELD *
  6798. get_dflt_custom_hdrs()
  6799. {
  6800.     PINEFIELD          *pfields, *pf;
  6801.     int            i;
  6802.  
  6803.     /* add one for possible use by fcc */
  6804.     i       = (count_custom_hdrs() + 2) * sizeof(PINEFIELD);
  6805.     pfields = (PINEFIELD *)fs_get((size_t) i);
  6806.     memset(pfields, 0, (size_t) i);
  6807.  
  6808.     /* set up the custom header pfields */
  6809.     customized_hdr_setup(pfields);
  6810.  
  6811.     return(pfields);
  6812. }
  6813.  
  6814.  
  6815. /*
  6816.  * free_customs - free misc. resources associated with custom header fields
  6817.  *
  6818.  *           pf - pointer to first custom field
  6819.  */
  6820. void
  6821. free_customs(head)
  6822. PINEFIELD *head;
  6823. {
  6824.     PINEFIELD *pf;
  6825.  
  6826.     for(pf = head; pf && pf->name; pf = pf->next){
  6827.  
  6828.     fs_give((void **)&pf->name);
  6829.  
  6830.     /* only true for FreeText */
  6831.     if(pf->textbuf)
  6832.       fs_give((void **)&pf->textbuf);
  6833.  
  6834.     /* only true for Address */
  6835.     if(pf->addr){
  6836.         if(*pf->addr)
  6837.           mail_free_address(pf->addr);
  6838.  
  6839.         fs_give((void **)&pf->addr);
  6840.     }
  6841.  
  6842.     if(pf->he && pf->he->prompt)
  6843.       fs_give((void **)&pf->he->prompt);
  6844.     }
  6845.  
  6846.     fs_give((void **)&head);
  6847. }
  6848.  
  6849.  
  6850.  
  6851. /**************** "PIPE" READING POSTING I/O ROUTINES ****************/
  6852.  
  6853.  
  6854. /*
  6855.  * helpful def's
  6856.  */
  6857. #define    S(X)        ((PIPE_S *)(X))
  6858. #define    GETBUFLEN    (4 * MAILTMPLEN)
  6859.  
  6860.  
  6861. /* 
  6862.  * pine_pipe_soutr - Replacement for tcp_soutr that writes one of our
  6863.  *             pipes rather than a tcp stream
  6864.  */
  6865. long
  6866. pine_pipe_soutr (stream,s)
  6867.      void *stream;
  6868.      char *s;
  6869. {
  6870.     int i, o;
  6871.  
  6872.     if(S(stream)->out.d < 0)
  6873.       return(0L);
  6874.  
  6875.     if(i = strlen(s)){
  6876.     while((o = write(S(stream)->out.d, s, i)) != i)
  6877.       if(o < 0){
  6878.           if(errno != EINTR){
  6879.           pine_pipe_abort(stream);
  6880.           return(0L);
  6881.           }
  6882.       }
  6883.       else{
  6884.           s += o;            /* try again, fix up counts */
  6885.           i -= o;
  6886.       }
  6887.     }
  6888.  
  6889.     return(1L);
  6890. }
  6891.  
  6892.  
  6893. /* 
  6894.  * pine_pipe_getline - Replacement for tcp_getline that reads one
  6895.  *               of our pipes rather than a tcp pipe
  6896.  *
  6897.  *                     C-client expects that the \r\n will be stripped off.
  6898.  */
  6899. char *
  6900. pine_pipe_getline (stream)
  6901.     void *stream;
  6902. {
  6903.     static int   cnt;
  6904.     static char *ptr;
  6905.     int         n, m;
  6906.     char    *ret, *s, *sp, c = '\0', d;
  6907.  
  6908.     if(S(stream)->in.d < 0)
  6909.       return(NULL);
  6910.  
  6911.     if(!S(stream)->tmp){        /* initialize! */
  6912.     /* alloc space to collect input (freed in close_system_pipe) */
  6913.     S(stream)->tmp = (char *) fs_get(GETBUFLEN);
  6914.     memset(S(stream)->tmp, 0, GETBUFLEN);
  6915.     cnt = -1;
  6916.     }
  6917.  
  6918.     while(cnt < 0){
  6919.     while((cnt = read(S(stream)->in.d, S(stream)->tmp, GETBUFLEN)) < 0)
  6920.       if(errno != EINTR){
  6921.           pine_pipe_abort(stream);
  6922.           return(NULL);
  6923.       }
  6924.  
  6925.     ptr = S(stream)->tmp;
  6926.     }
  6927.  
  6928.     s = ptr;
  6929.     n = 0;
  6930.     while(cnt--){
  6931.     d = *ptr++;
  6932.     if((c == '\015') && (d == '\012')){
  6933.         ret = (char *)fs_get (n--);
  6934.         memcpy(ret, s, n);
  6935.         ret[n] = '\0';
  6936.         return(ret);
  6937.     }
  6938.  
  6939.     n++;
  6940.     c = d;
  6941.     }
  6942.                     /* copy partial string from buffer */
  6943.     memcpy((ret = sp = (char *) fs_get (n)), s, n);
  6944.                     /* get more data */
  6945.     while(cnt < 0){
  6946.     while((cnt = read(S(stream)->in.d, S(stream)->tmp, GETBUFLEN)) < 0)
  6947.       if(errno != EINTR){
  6948.           fs_give((void **) &ret);
  6949.           pine_pipe_abort(stream);
  6950.           return(NULL);
  6951.       }
  6952.  
  6953.     ptr = S(stream)->tmp;
  6954.     }
  6955.  
  6956.     if(c == '\015' && *ptr == '\012'){
  6957.     ptr++;
  6958.     cnt--;
  6959.     ret[n - 1] = '\0';        /* tie off string with null */
  6960.     }
  6961.     else if (s = pine_pipe_getline(stream)) {
  6962.     ret = (char *) fs_get(n + 1 + (m = strlen (s)));
  6963.     memcpy(ret, sp, n);        /* copy first part */
  6964.     memcpy(ret + n, s, m);        /* and second part */
  6965.     fs_give((void **) &sp);        /* flush first part */
  6966.     fs_give((void **) &s);        /* flush second part */
  6967.     ret[n + m] = '\0';        /* tie off string with null */
  6968.     }
  6969.  
  6970.     return(ret);
  6971. }
  6972.  
  6973.  
  6974. /* 
  6975.  * pine_pipe_close - Replacement for tcp_close that closes pipes to our
  6976.  *             child rather than a tcp connection
  6977.  */
  6978. void
  6979. pine_pipe_close(stream)
  6980.     void *stream;
  6981. {
  6982.     /*
  6983.      * Uninstall our hooks into smtp_send since it's being used by
  6984.      * the nntp driver as well...
  6985.      */
  6986.     (void) mail_parameters(NULL, SET_POSTSOUTR, sending_hooks.soutr);
  6987.     (void) mail_parameters(NULL, SET_POSTGETLINE, sending_hooks.getline);
  6988.     (void) mail_parameters(NULL, SET_POSTCLOSE, sending_hooks.close);
  6989.     (void) close_system_pipe((PIPE_S **) &stream);
  6990. }
  6991.  
  6992.  
  6993. /*
  6994.  * pine_pipe_abort - close down the pipe we were using to post
  6995.  */
  6996. void
  6997. pine_pipe_abort(stream)
  6998.     void *stream;
  6999. {
  7000.     if(S(stream)->in.d >= 0){
  7001.     close(S(stream)->in.d);
  7002.     S(stream)->in.d = -1;
  7003.     }
  7004.  
  7005.     if(S(stream)->out.d){
  7006.     close(S(stream)->out.d);
  7007.     S(stream)->out.d = -1;
  7008.     }
  7009. }
  7010.